Support setting the X3D V-Cache Mode

This commit is contained in:
cjwilsontech
2025-09-03 20:38:56 -07:00
parent 3c024dd6d9
commit e98e6837bb
9 changed files with 467 additions and 3 deletions

View File

@@ -114,6 +114,8 @@ struct GameModeConfig {
char cpu_park_cores[CONFIG_VALUE_MAX];
char cpu_pin_cores[CONFIG_VALUE_MAX];
char amd_x3d_mode_desired[CONFIG_VALUE_MAX];
char amd_x3d_mode_default[CONFIG_VALUE_MAX];
long require_supervisor;
char supervisor_whitelist[CONFIG_LIST_MAX][CONFIG_VALUE_MAX];
@@ -242,6 +244,23 @@ static bool get_string_value(const char *value, char output[CONFIG_VALUE_MAX])
return true;
}
/*
* Get and validate an x3d mode value
*/
static bool get_x3d_mode_value(const char *name, const char *value, char output[CONFIG_VALUE_MAX])
{
if (strcmp(value, "frequency") != 0 && strcmp(value, "cache") != 0) {
LOG_ERROR("Config: %s has invalid value '%s'. Valid values are 'frequency' or 'cache'\n",
name,
value);
return false;
}
strncpy(output, value, CONFIG_VALUE_MAX - 1);
output[CONFIG_VALUE_MAX - 1] = '\0';
return true;
}
/* Controls whether to read the protected config variables */
static bool load_protected = false;
@@ -316,6 +335,10 @@ static int inih_handler(void *user, const char *section, const char *name, const
valid = get_string_value(value, self->values.cpu_park_cores);
} else if (strcmp(name, "pin_cores") == 0) {
valid = get_string_value(value, self->values.cpu_pin_cores);
} else if (strcmp(name, "amd_x3d_mode_desired") == 0) {
valid = get_x3d_mode_value(name, value, self->values.amd_x3d_mode_desired);
} else if (strcmp(name, "amd_x3d_mode_default") == 0) {
valid = get_x3d_mode_value(name, value, self->values.amd_x3d_mode_default);
}
} else if (strcmp(section, "supervisor") == 0) {
/* Supervisor subsection */
@@ -861,6 +884,22 @@ void config_get_cpu_pin_cores(GameModeConfig *self, char value[CONFIG_VALUE_MAX]
sizeof(self->values.cpu_pin_cores));
}
void config_get_amd_x3d_mode_desired(GameModeConfig *self, char value[CONFIG_VALUE_MAX])
{
memcpy_locked_config(self,
value,
&self->values.amd_x3d_mode_desired,
sizeof(self->values.amd_x3d_mode_desired));
}
void config_get_amd_x3d_mode_default(GameModeConfig *self, char value[CONFIG_VALUE_MAX])
{
memcpy_locked_config(self,
value,
&self->values.amd_x3d_mode_default,
sizeof(self->values.amd_x3d_mode_default));
}
/*
* Checks if the supervisor is whitelisted
*/

View File

@@ -126,6 +126,8 @@ void config_get_amd_performance_level(GameModeConfig *self, char value[CONFIG_VA
*/
void config_get_cpu_park_cores(GameModeConfig *self, char value[CONFIG_VALUE_MAX]);
void config_get_cpu_pin_cores(GameModeConfig *self, char value[CONFIG_VALUE_MAX]);
void config_get_amd_x3d_mode_desired(GameModeConfig *self, char value[CONFIG_VALUE_MAX]);
void config_get_amd_x3d_mode_default(GameModeConfig *self, char value[CONFIG_VALUE_MAX]);
/**
* Functions to get supervisor config permissions

View File

@@ -105,6 +105,8 @@ struct GameModeContext {
long initial_split_lock_mitigate;
char initial_x3d_mode[64]; /**<Initial x3d mode to restore */
/* Reaper control */
struct {
pthread_t thread;
@@ -168,6 +170,9 @@ void game_mode_context_init(GameModeContext *self)
self->initial_split_lock_mitigate = -1;
/* clear the initial x3d mode string */
memset(self->initial_x3d_mode, 0, sizeof(self->initial_x3d_mode));
pthread_rwlock_init(&self->rwlock, NULL);
/* Get the reaper thread going */
@@ -256,6 +261,89 @@ static int game_mode_disable_splitlock(GameModeContext *self, bool disable)
return 0;
}
static void game_mode_store_x3d_mode(GameModeContext *self)
{
char x3d_mode_desired[CONFIG_VALUE_MAX] = { 0 };
config_get_amd_x3d_mode_desired(self->config, x3d_mode_desired);
if (x3d_mode_desired[0] == '\0') {
return;
}
if (access(LIBEXECDIR "/x3dmodectl", X_OK) != 0) {
LOG_MSG("x3dmodectl utility not found, X3D mode control disabled\n");
return;
}
const char *const exec_args[] = {
LIBEXECDIR "/x3dmodectl",
"get",
NULL,
};
char output[EXTERNAL_BUFFER_MAX] = { 0 };
int ret = run_external_process(exec_args, output, -1);
if (ret != 0) {
LOG_MSG("X3D mode hardware not available or failed to get current mode\n");
return;
}
strncpy(self->initial_x3d_mode, output, sizeof(self->initial_x3d_mode) - 1);
self->initial_x3d_mode[sizeof(self->initial_x3d_mode) - 1] = '\0';
char *newline = strchr(self->initial_x3d_mode, '\n');
if (newline) {
*newline = '\0';
}
LOG_MSG("x3d mode was initially set to [%s]\n", self->initial_x3d_mode);
}
static int game_mode_set_x3d_mode(GameModeContext *self, bool desired)
{
char x3d_mode_config[CONFIG_VALUE_MAX] = { 0 };
if (desired) {
config_get_amd_x3d_mode_desired(self->config, x3d_mode_config);
} else {
config_get_amd_x3d_mode_default(self->config, x3d_mode_config);
if (x3d_mode_config[0] == '\0') {
if (self->initial_x3d_mode[0] != '\0') {
strncpy(x3d_mode_config, self->initial_x3d_mode, CONFIG_VALUE_MAX - 1);
x3d_mode_config[CONFIG_VALUE_MAX - 1] = '\0';
} else {
return 0;
}
}
}
if (x3d_mode_config[0] == '\0') {
return 0;
}
if (access(LIBEXECDIR "/x3dmodectl", X_OK) != 0) {
LOG_MSG("x3dmodectl utility not found, skipping X3D mode change\n");
return 0;
}
if (strcmp(x3d_mode_config, "frequency") != 0 && strcmp(x3d_mode_config, "cache") != 0) {
LOG_ERROR("Invalid X3D mode '%s'. Valid modes are 'frequency' or 'cache'\n",
x3d_mode_config);
return -1;
}
const char *const exec_args[] = {
"pkexec", LIBEXECDIR "/x3dmodectl", "set", x3d_mode_config, NULL,
};
LOG_MSG("Requesting update of X3D mode to %s\n", x3d_mode_config);
int ret = run_external_process(exec_args, NULL, -1);
if (ret != 0) {
LOG_ERROR("Failed to update X3D mode\n");
return ret;
}
return 0;
}
static void game_mode_store_governor(GameModeContext *self)
{
if (self->current_govenor != GAME_MODE_GOVERNOR_DEFAULT)
@@ -468,6 +556,8 @@ static void game_mode_context_store_defaults(GameModeContext *self)
game_mode_store_governor(self);
game_mode_store_splitlock(self);
game_mode_store_x3d_mode(self);
}
/**
@@ -504,6 +594,8 @@ static void game_mode_context_enter(GameModeContext *self)
game_mode_disable_splitlock(self, true);
game_mode_set_x3d_mode(self, true);
/* Apply GPU optimisations by first getting the current values, and then setting the target */
game_mode_get_gpu(self->stored_gpu);
game_mode_apply_gpu(self->target_gpu);
@@ -548,6 +640,8 @@ static void game_mode_context_leave(GameModeContext *self)
game_mode_disable_splitlock(self, false);
game_mode_set_x3d_mode(self, false);
game_mode_set_governor(self, GAME_MODE_GOVERNOR_DEFAULT);
game_mode_disable_igpu_optimization(self);

View File

@@ -42,6 +42,8 @@ POSSIBILITY OF SUCH DAMAGE.
#include "gamemode-config.h"
#include "gamemode_client.h"
#include "build-config.h"
#include <pthread.h>
#include <sys/syscall.h>
#include <sys/wait.h>
@@ -837,6 +839,106 @@ int run_ioprio_tests(struct GameModeConfig *config)
return ret;
}
/* Check the AMD X3D mode setting works */
static int run_x3d_mode_tests(struct GameModeConfig *config)
{
/* Get the two config parameters we care about */
char desired_mode[CONFIG_VALUE_MAX] = { 0 };
config_get_amd_x3d_mode_desired(config, desired_mode);
if (desired_mode[0] == '\0') {
/* Not configured */
return 1;
}
char default_mode[CONFIG_VALUE_MAX] = { 0 };
config_get_amd_x3d_mode_default(config, default_mode);
/* Get the initial X3D mode state */
char initial_mode[64] = { 0 };
const char *const get_args[] = {
LIBEXECDIR "/x3dmodectl",
"get",
NULL,
};
char output[EXTERNAL_BUFFER_MAX] = { 0 };
int ret = run_external_process(get_args, output, -1);
if (ret != 0) {
return 1;
}
/* Store the initial mode, removing any trailing newline */
strncpy(initial_mode, output, sizeof(initial_mode) - 1);
initial_mode[sizeof(initial_mode) - 1] = '\0';
char *newline = strchr(initial_mode, '\n');
if (newline) {
*newline = '\0';
}
/* Check if hardware is available */
if (strcmp(initial_mode, "unavailable") == 0) {
return 1;
}
/* Start gamemode */
gamemode_request_start();
/* Give gamemode time to apply settings */
usleep(500000);
/* Verify the mode is the desired one */
ret = run_external_process(get_args, output, -1);
if (ret != 0) {
LOG_ERROR("Failed to get X3D mode after gamemode start\n");
gamemode_request_end();
return -1;
}
/* Remove trailing newline from output */
newline = strchr(output, '\n');
if (newline) {
*newline = '\0';
}
if (strcmp(output, desired_mode) != 0) {
LOG_ERROR("X3D mode was not set to %s (was actually %s)!\n", desired_mode, output);
gamemode_request_end();
return -1;
}
/* End gamemode */
gamemode_request_end();
/* Give gamemode time to restore settings */
usleep(500000);
/* Verify the mode is restored */
ret = run_external_process(get_args, output, -1);
if (ret != 0) {
LOG_ERROR("Failed to get X3D mode after gamemode end\n");
return -1;
}
/* Remove trailing newline from output */
newline = strchr(output, '\n');
if (newline) {
*newline = '\0';
}
/* Determine expected restored mode */
const char *expected_mode = (default_mode[0] != '\0') ? default_mode : initial_mode;
if (strcmp(output, expected_mode) != 0) {
LOG_ERROR("X3D mode was not restored to %s (was actually %s)!\n", expected_mode, output);
return -1;
}
return 0;
}
/**
* game_mode_run_feature_tests runs a set of tests for each current feature (based on the current
* config) returns 0 for success, -1 for failure
@@ -947,6 +1049,21 @@ static int game_mode_run_feature_tests(struct GameModeConfig *config)
}
}
/* Was the AMD X3D mode changed? */
{
LOG_MSG("::: Verifying AMD X3D mode\n");
int x3dstatus = run_x3d_mode_tests(config);
if (x3dstatus == 1)
LOG_MSG("::: Passed (AMD X3D mode not configured)\n");
else if (x3dstatus == 0)
LOG_MSG("::: Passed\n");
else {
LOG_MSG("::: Failed!\n");
status = -1;
}
}
/* TODO */
/* Was the scheduling applied and removed? Does it get applied to a full process tree? */
/* Does the screensaver get inhibited? Unknown if this is testable, org.freedesktop.ScreenSaver