1
0
mirror of https://github.com/djhackersdev/bemanitools.git synced 2025-02-21 21:00:02 +01:00

feat: nvgpu, add option to enable gpu scaling (#329)

GPU scaling allows high quality upscaling of older games
to the native resolution of the screen. The output frame
quality is significantly sharper than the fairly simple
frame buffer upscaling of bemanitools.

Co-authored-by: icex2 <djh.icex2@gmail.com>
This commit is contained in:
icex2 2025-02-13 15:03:34 +01:00 committed by GitHub
parent 233df32372
commit 972acb5f0e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -30,6 +30,11 @@
nv_api->NvAPI_GetErrorMessage(status, error_str); \
fprintf(stderr, fmt ", reason: %s\n", ##__VA_ARGS__, error_str);
typedef struct displayconfig_path_info {
NvU32 path_info_count;
NV_DISPLAYCONFIG_PATH_INFO *path_info;
} displayconfig_path_info_t;
static bool _session_destroy(const nv_api_t *nv_api, NvDRSSessionHandle session)
{
assert(nv_api);
@ -140,6 +145,150 @@ static bool _profile_setting_set(
return true;
}
static void _display_config_free(displayconfig_path_info_t *displayconfig_path_info)
{
assert(displayconfig_path_info);
assert(displayconfig_path_info->path_info);
for (NvU32 i = 0; i < displayconfig_path_info->path_info_count; i++) {
assert(displayconfig_path_info->path_info[i].sourceModeInfo);
assert(displayconfig_path_info->path_info[i].targetInfo);
for (NvU32 j = 0; j < displayconfig_path_info->path_info[i].targetInfoCount; j++) {
assert(displayconfig_path_info->path_info[i].targetInfo[i].details);
free(displayconfig_path_info->path_info[i].targetInfo[i].details);
}
free(displayconfig_path_info->path_info[i].targetInfo);
free(displayconfig_path_info->path_info[i].sourceModeInfo);
}
free(displayconfig_path_info->path_info);
}
static bool _display_config_get(const nv_api_t *nv_api, displayconfig_path_info_t *displayconfig_path_info)
{
NvAPI_Status status;
assert(nv_api);
assert(displayconfig_path_info);
// TODO we need a total of three calls to GetDisplayConfig: https://github.com/NVIDIA/nvapi/blob/d08488fcc82eef313b0464db37d2955709691e94/Sample_Code/DisplayConfiguration/DisplayConfiguration.cpp#L68
status = nv_api->NvAPI_DISP_GetDisplayConfig(&displayconfig_path_info->path_info_count , NULL);
if (status != NVAPI_OK) {
PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Getting display config path info count");
return false;
}
displayconfig_path_info->path_info = (NV_DISPLAYCONFIG_PATH_INFO*)
xmalloc(sizeof(NV_DISPLAYCONFIG_PATH_INFO) * displayconfig_path_info->path_info_count);
memset(displayconfig_path_info->path_info, 0, sizeof(NV_DISPLAYCONFIG_PATH_INFO) * displayconfig_path_info->path_info_count);
for (NvU32 i = 0; i < displayconfig_path_info->path_info_count; i++) {
displayconfig_path_info->path_info[i].version = NV_DISPLAYCONFIG_PATH_INFO_VER;
}
status = nv_api->NvAPI_DISP_GetDisplayConfig(&displayconfig_path_info->path_info_count, displayconfig_path_info->path_info);
if (status != NVAPI_OK) {
PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Getting display config target info counts");
free(displayconfig_path_info->path_info);
return false;
}
for (NvU32 i = 0; i < displayconfig_path_info->path_info_count; i++) {
if (displayconfig_path_info->path_info[i].version == NV_DISPLAYCONFIG_PATH_INFO_VER1 ||
displayconfig_path_info->path_info[i].version == NV_DISPLAYCONFIG_PATH_INFO_VER2) {
displayconfig_path_info->path_info[i].sourceModeInfo =
(NV_DISPLAYCONFIG_SOURCE_MODE_INFO*) xmalloc(sizeof(NV_DISPLAYCONFIG_SOURCE_MODE_INFO));
} else {
#ifdef NV_DISPLAYCONFIG_PATH_INFO_VER3
displayconfig_path_info->path_info[i].sourceModeInfo =(NV_DISPLAYCONFIG_SOURCE_MODE_INFO*) malloc(
displayconfig_path_info->path_info[i].sourceModeInfoCount * sizeof(NV_DISPLAYCONFIG_SOURCE_MODE_INFO));
#endif
}
memset(displayconfig_path_info->path_info[i].sourceModeInfo, 0, sizeof(NV_DISPLAYCONFIG_SOURCE_MODE_INFO));
displayconfig_path_info->path_info[i].targetInfo = (NV_DISPLAYCONFIG_PATH_TARGET_INFO*) xmalloc(
displayconfig_path_info->path_info[i].targetInfoCount * sizeof(NV_DISPLAYCONFIG_PATH_TARGET_INFO));
memset(displayconfig_path_info->path_info[i].targetInfo, 0,
displayconfig_path_info->path_info[i].targetInfoCount * sizeof(NV_DISPLAYCONFIG_PATH_TARGET_INFO));
for (NvU32 j = 0 ; j < displayconfig_path_info->path_info[i].targetInfoCount ; j++) {
displayconfig_path_info->path_info[i].targetInfo[j].details =
(NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO*) xmalloc(sizeof(NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO));
memset(displayconfig_path_info->path_info[i].targetInfo[j].details, 0,
sizeof(NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO));
displayconfig_path_info->path_info[i].targetInfo[j].details->version =
NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_VER;
}
}
status = nv_api->NvAPI_DISP_GetDisplayConfig(&displayconfig_path_info->path_info_count, displayconfig_path_info->path_info);
if (status != NVAPI_OK) {
PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Getting display config");
_display_config_free(displayconfig_path_info);
return false;
}
return true;
}
static bool _display_config_set(const nv_api_t *nv_api, displayconfig_path_info_t *displayconfig_path_info)
{
NvAPI_Status status;
assert(nv_api);
assert(displayconfig_path_info);
printfln_err("Validating display config...");
status = nv_api->NvAPI_DISP_SetDisplayConfig(displayconfig_path_info->path_info_count, displayconfig_path_info->path_info, NV_DISPLAYCONFIG_VALIDATE_ONLY);
if (status != NVAPI_OK) {
PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Validating display config");
return false;
}
printfln_err("Saving display config...");
status = nv_api->NvAPI_DISP_SetDisplayConfig(displayconfig_path_info->path_info_count, displayconfig_path_info->path_info, NV_DISPLAYCONFIG_SAVE_TO_PERSISTENCE | NV_DISPLAYCONFIG_DRIVER_RELOAD_ALLOWED);
if (status != NVAPI_OK) {
PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Saving display config");
return false;
}
return true;
}
static bool _display_config_visit(displayconfig_path_info_t *displayconfig_path_info, NvU32 display_id, bool (*display_config_filter)(NV_DISPLAYCONFIG_PATH_TARGET_INFO *target_info))
{
NV_DISPLAYCONFIG_PATH_TARGET_INFO *target_info;
for (NvU32 i = 0; i < displayconfig_path_info->path_info_count; i++) {
target_info = &displayconfig_path_info->path_info[i].targetInfo[i];
if (display_id != 0 && target_info->displayId != display_id) {
continue;
}
return display_config_filter(target_info);
}
return false;
}
static void _ensure_drs_settings_folder_exists()
{
// Even on a fresh install, this might not exist which result
@ -368,6 +517,58 @@ static bool _profile_gpu_power_state_max(const nv_api_t *nv_api, const char *pro
return _settings_save_and_session_destroy(nv_api, session);
}
static bool _display_primary_display_id(const nv_api_t *nv_api)
{
NvAPI_Status status;
NvPhysicalGpuHandle gpu_handle[NVAPI_MAX_PHYSICAL_GPUS];
NvU32 gpu_count;
NvU32 display_id_count;
NV_GPU_DISPLAYIDS *display_ids;
assert(nv_api);
status = nv_api->NvAPI_EnumPhysicalGPUs(gpu_handle, &gpu_count);
if (status != NVAPI_OK) {
PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Enumerating physical GPUs");
return false;
}
// Assuming first physical GPU, first display = primary display
if (gpu_count > 0) {
status = nv_api->NvAPI_GPU_GetConnectedDisplayIds(gpu_handle[0], NULL, &display_id_count, 0);
if (status != NVAPI_OK) {
PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Getting connected display count of GPU");
return false;
}
if (display_id_count > 0) {
display_ids = (NV_GPU_DISPLAYIDS*) xmalloc(sizeof(NV_GPU_DISPLAYIDS) * display_id_count);
for (NvU32 j = 0; j < display_id_count; j++) {
display_ids[j].version = NV_GPU_DISPLAYIDS_VER;
}
status = nv_api->NvAPI_GPU_GetConnectedDisplayIds(gpu_handle[0], display_ids, &display_id_count, 0);
if (status != NVAPI_OK) {
free(display_ids);
PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Getting connected display ids of GPU");
return false;
}
printfln_out("0x%lX", display_ids[0].displayId);
free(display_ids);
return true;
}
}
return false;
}
static bool _displays_list(const nv_api_t *nv_api)
{
NvAPI_Status status;
@ -459,56 +660,35 @@ static bool _displays_list(const nv_api_t *nv_api)
return true;
}
static bool _display_config_get(const nv_api_t *nv_api, NvU32 display_id)
static bool _display_config_print(const nv_api_t *nv_api, NvU32 display_id)
{
NvU32 target_info_count;
NV_DISPLAYCONFIG_PATH_INFO display_config;
NvAPI_Status status;
displayconfig_path_info_t displayconfig_path_info;
NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2 *target_info;
NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1 *source_mode_info;
assert(nv_api);
status = nv_api->NvAPI_DISP_GetDisplayConfig(&target_info_count, NULL);
if (status != NVAPI_OK) {
PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Getting display config count");
return false;
}
memset(&display_config, 0, sizeof(NV_DISPLAYCONFIG_PATH_INFO));
display_config.version = NV_DISPLAYCONFIG_PATH_INFO_VER;
display_config.targetInfoCount = target_info_count;
display_config.targetInfo = (NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2*) xmalloc(sizeof(NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2) * target_info_count);
display_config.sourceModeInfo = (NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1*) xmalloc(sizeof(NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1) * target_info_count);
status = nv_api->NvAPI_DISP_GetDisplayConfig(&target_info_count, &display_config);
if (status != NVAPI_OK) {
PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Getting display config");
free(display_config.targetInfo);
free(display_config.sourceModeInfo);
return false;
}
_display_config_get(nv_api, &displayconfig_path_info);
if (display_id != 0) {
printfln_err("Applying display ID filter: %lX", display_id);
}
printfln_err("Num total display configs: %ld", display_config.targetInfoCount);
printfln_err("Num total adapters in config: %ld", displayconfig_path_info.path_info_count);
for (NvU32 i = 0; i < display_config.targetInfoCount; i++) {
target_info = &display_config.targetInfo[i];
source_mode_info = &display_config.sourceModeInfo[i];
for (NvU32 i = 0; i < displayconfig_path_info.path_info_count; i++) {
printfln_err("Num total displays in adapter: %ld", displayconfig_path_info.path_info[i].targetInfoCount);
for (NvU32 j = 0; j < displayconfig_path_info.path_info[i].targetInfoCount; j++) {
target_info = &displayconfig_path_info.path_info[i].targetInfo[j];
source_mode_info = &displayconfig_path_info.path_info[i].sourceModeInfo[j];
if (display_id != 0 && target_info->displayId != display_id) {
continue;
}
printfln_out("--------------------------------");
printfln_out("Adapter source ID %ld", displayconfig_path_info.path_info[i].sourceId);
printfln_out("Display ID: %lX", target_info->displayId);
printfln_out("Resolution width: %ld", source_mode_info->resolution.width);
printfln_out("Resolution height: %ld", source_mode_info->resolution.height);
@ -943,9 +1123,9 @@ static bool _display_config_get(const nv_api_t *nv_api, NvU32 display_id)
printfln_out("--------------------------------");
}
}
free(display_config.targetInfo);
free(display_config.sourceModeInfo);
_display_config_free(&displayconfig_path_info);
return true;
}
@ -1033,6 +1213,48 @@ static bool _custom_resolution_set(
return true;
}
static bool _display_config_gpu_scaling_enable_filter(NV_DISPLAYCONFIG_PATH_TARGET_INFO *target_info)
{
if (target_info->details == NULL) {
printfln_err("ERROR: No details found for target info");
return false;
}
if (target_info->details->scaling == NV_SCALING_GPU_SCALING_TO_NATIVE) {
printfln_err("GPU scaling to native resolution is already enabled");
return false;
} else {
target_info->details->scaling = NV_SCALING_GPU_SCALING_TO_NATIVE;
printfln_err("Enabling GPU scaling to native resolution");
return true;
}
}
static bool _display_config_gpu_scaling_to_native_resolution_enable(const nv_api_t *nv_api, NvU32 display_id)
{
displayconfig_path_info_t displayconfig_path_info;
bool changes_to_save;
assert(nv_api);
if (!_display_config_get(nv_api, &displayconfig_path_info)) {
return false;
}
changes_to_save = _display_config_visit(&displayconfig_path_info, display_id, _display_config_gpu_scaling_enable_filter);
if (changes_to_save) {
if (!_display_config_set(nv_api, &displayconfig_path_info)) {
_display_config_free(&displayconfig_path_info);
return false;
}
}
_display_config_free(&displayconfig_path_info);
return true;
}
// -------------------------------------------------------------------------------------------------
static void _print_synopsis()
@ -1051,13 +1273,15 @@ static void _print_synopsis()
printfln_err(" gpu-power-state-max <profile_name> Set GPU power state to maximum for the driver profile");
printfln_err("");
printfln_err(" display");
printfln_err(" primary-display-id Print the ID of the primary display");
printfln_err(" list List all connected displays and their display IDs");
printfln_err(" config-get [display_id] Get the current display configurations. Optionally, specify a display ID to get the configuration of that display only");
printfln_err(" config [display_id] Print the current display configurations. Optionally, specify a display ID to get the configuration of that display only");
printfln_err(" custom-resolution-set <display_id> <screen_width> <screen_height> <screen_refresh_rate>");
printfln_err(" Set a custom display mode with the given parameters for the given display ID. The settings are persisted immediately. Ensure you tested these before with the custom-display-test command.");
printfln_err(" custom-resolution-test <display_id> <screen_width> <screen_height> <screen_refresh_rate> [--test-timeout-secs n]");
printfln_err(" Test a custom display mode for a limited amount of time. This will revert the display mode after the given amount of seconds and not persist the changes.");
printfln_err(" test-timeout-secs: Optional. Number of seconds to test the custom display mode for. Default is 10 seconds.");
printfln_err(" gpu-scaling-to-native-resolution-enable <display_id> Enable GPU scaling to native resolution for the given display ID");
}
static bool _cmd_nv_info(const nv_api_t *nv_api)
@ -1142,12 +1366,17 @@ static bool _cmd_profile_gpu_power_state_max(const nv_api_t *nv_api, int argc, c
return _profile_gpu_power_state_max(nv_api, profile_name);
}
static bool _cmd_display_primary_display_id(const nv_api_t *nv_api)
{
return _display_primary_display_id(nv_api);
}
static bool _cmd_display_list(const nv_api_t *nv_api)
{
return _displays_list(nv_api);
}
static bool _cmd_display_config_get(const nv_api_t *nv_api, int argc, char **argv)
static bool _cmd_display_config(const nv_api_t *nv_api, int argc, char **argv)
{
uint32_t display_id;
@ -1161,7 +1390,7 @@ static bool _cmd_display_config_get(const nv_api_t *nv_api, int argc, char **arg
display_id = 0;
}
return _display_config_get(nv_api, display_id);
return _display_config_print(nv_api, display_id);
}
static bool _cmd_custom_resolution_set(const nv_api_t *nv_api, int argc, char **argv)
@ -1252,6 +1481,25 @@ static bool _cmd_custom_resolution_test(const nv_api_t *nv_api, int argc, char *
test_only_timeout_sec);
}
static bool _cmd_display_gpu_scaling_to_native_resolution_enable(const nv_api_t *nv_api, int argc, char **argv)
{
uint32_t display_id;
if (argc < 1) {
_print_synopsis();
printfln_err("ERROR: Insufficient arguments");
return false;
}
if (argv[0][0] == '0' && argv[0][1] == 'x') {
display_id = strtoul(argv[0] + 2, NULL, 16);
} else {
display_id = strtoul(argv[0], NULL, 10);
}
return _display_config_gpu_scaling_to_native_resolution_enable(nv_api, display_id);
}
// -------------------------------------------------------------------------------------------------
int main(int argc, char **argv)
@ -1319,14 +1567,18 @@ int main(int argc, char **argv)
result = false;
}
} else if (!strcmp(command, "display")) {
if (!strcmp(sub_command, "list")) {
if (!strcmp(sub_command, "primary-display-id")) {
result = _cmd_display_primary_display_id(&nv_api);
} else if (!strcmp(sub_command, "list")) {
result = _cmd_display_list(&nv_api);
} else if (!strcmp(sub_command, "config-get")) {
result = _cmd_display_config_get(&nv_api, argc - 3, argv + 3);
} else if (!strcmp(sub_command, "config")) {
result = _cmd_display_config(&nv_api, argc - 3, argv + 3);
} else if (!strcmp(sub_command, "custom-resolution-set")) {
result = _cmd_custom_resolution_set(&nv_api, argc - 3, argv + 3);
} else if (!strcmp(sub_command, "custom-resolution-test")) {
result = _cmd_custom_resolution_test(&nv_api, argc - 3, argv + 3);
} else if (!strcmp(sub_command, "gpu-scaling-to-native-resolution-enable")) {
result = _cmd_display_gpu_scaling_to_native_resolution_enable(&nv_api, argc - 3, argv + 3);
} else {
printfln_err("ERROR: Unknown sub-command: %s", sub_command);
_print_synopsis();