Skip to content

Commit

Permalink
Add option to save previous value
Browse files Browse the repository at this point in the history
  • Loading branch information
FrogTheFrog committed Dec 17, 2023
1 parent d7a75ec commit 7da9b80
Show file tree
Hide file tree
Showing 3 changed files with 237 additions and 22 deletions.
8 changes: 6 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@
"xlocmes": "cpp",
"xlocmon": "cpp",
"xloctime": "cpp",
"regex": "cpp"
}
"regex": "cpp",
"ranges": "cpp",
"fstream": "cpp",
"codecvt": "cpp"
},
"C_Cpp.default.configurationProvider": "go2sh.cmake-integration"
}
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,19 @@
App for Windows to toggle Nvidia's Frame Rate Limiter (FRL) via command line

# How to use
### ---> Make sure that the global frame rate limiter setting has been saved at least once via the Nvidia control panel! <---

----

Run the app in terminal for usage instruction, but in case you're lazy:
```
Usage example:
frltoggle status prints the current FRL value. Value 0 means it's disabled.
frltoggle 0 turns off the framerate limiter.
frltoggle 60 sets the FPS limit to 60 (allowed values are [0, 1023]).
frltoggle status prints the current FRL value. Value 0 means it's disabled.
frltoggle 0 turns off the framerate limiter.
frltoggle 60 sets the FPS limit to 60 (allowed values are [0, 1023]).
frltoggle 60 --save-previous sets the FPS limit to 60 and saves the previous value to a file.
frltoggle 60 --save-previous-or-reuse sets the FPS limit to 60 and saves the previous value to a file.
If the file already exists, its value will be validated and reused instead.
frltoggle load-file loads the value from file (e.g., saved using "--save-previous") and uses it to set FRL.
File is removed afterwards if no errors occurred.
```
237 changes: 220 additions & 17 deletions src/main.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
// system includes
#include <filesystem>
#include <format>
#include <fstream>
#include <iostream>
#include <optional>
#include <regex>
#include <set>
#include <stdexcept>
#include <variant>
#include <vector>

// local includes
Expand All @@ -12,9 +16,51 @@

//--------------------------------------------------------------------------------------------------

bool parseFpsNumber(const std::string& arg, unsigned long& number)
namespace
{
bool valid_number{false};
struct StatusOption
{
};

//--------------------------------------------------------------------------------------------------

struct SetFpsOption
{
enum class SaveOption
{
No,
SavePrevious,
SavePreviousAndReuse
};

unsigned long m_fps_value{0};
SaveOption m_save{SaveOption::No};
};

//--------------------------------------------------------------------------------------------------

struct LoadFileOption
{
};

//--------------------------------------------------------------------------------------------------

std::string trim(std::string value)
{
value.erase(std::find_if(value.rbegin(), value.rend(), [](auto ch) { return !std::isspace(ch); }).base(),
value.end());

value.erase(value.begin(), std::find_if(value.begin(), value.end(), [](auto ch) { return !std::isspace(ch); }));

return value;
}

//--------------------------------------------------------------------------------------------------

std::optional<unsigned long> parseFpsNumber(const std::string& arg, bool no_throw = false)
{
bool valid_number{false};
unsigned long number{0};

try
{
Expand All @@ -29,34 +75,153 @@ bool parseFpsNumber(const std::string& arg, unsigned long& number)
{
if ((number < FRL_FPS_MIN || number > FRL_FPS_MAX) && number != FRL_FPS_DISABLED)
{
if (no_throw)
{
return std::nullopt;
}

throw std::runtime_error(std::format(
"FPS value {} is outside the range of [{}, {}] and is not {} (for disabling)!", number,
static_cast<int>(FRL_FPS_MIN), static_cast<int>(FRL_FPS_MAX), static_cast<int>(FRL_FPS_DISABLED)));
}

return number;
}

return valid_number;
return std::nullopt;
}

//--------------------------------------------------------------------------------------------------

auto parseArgs(const std::vector<std::string>& args)
-> std::optional<std::variant<StatusOption, SetFpsOption, LoadFileOption>>
{
if (args.size() < 2)
{
return std::nullopt;
}

const std::string first_arg{trim(args[1])};
{
if (first_arg == "status")
{
if (args.size() > 2)
{
return std::nullopt;
}

return StatusOption{};
}

if (first_arg == "load-file")
{
if (args.size() > 2)
{
return std::nullopt;
}

return LoadFileOption{};
}

if (auto fps_number = parseFpsNumber(first_arg); fps_number)
{
if (args.size() > 3)
{
return std::nullopt;
}
else if (args.size() < 3)
{
return SetFpsOption{*fps_number};
}

const std::string second_arg{trim(args[2])};
if (second_arg == "--save-previous")
{
return SetFpsOption{*fps_number, SetFpsOption::SaveOption::SavePrevious};
}

if (second_arg == "--save-previous-or-reuse")
{
return SetFpsOption{*fps_number, SetFpsOption::SaveOption::SavePreviousAndReuse};
}

return std::nullopt;
}
}

return std::nullopt;
}

//--------------------------------------------------------------------------------------------------

std::optional<unsigned long> read_fps_from_file(const std::string& filename, bool no_throw = false)
{
std::ifstream file;
file.open(filename);

if (!file)
{
if (no_throw)
{
return std::nullopt;
}

throw std::runtime_error(std::format("Failed to open file {} for reading", filename));
}

std::string line;
file >> line;

return parseFpsNumber(trim(line), no_throw);
}

//--------------------------------------------------------------------------------------------------

void save_to_file(const std::string& filename, unsigned long fps_value)
{
std::ofstream file;
file.open(filename);

if (!file)
{
throw std::runtime_error(std::format("Failed to open file {} for writting", filename));
}

file << fps_value;
}

//--------------------------------------------------------------------------------------------------

void remove_file(const std::string& filename)
{
// Note: will throw on actual error
std::filesystem::remove(filename);
}
} // namespace

//--------------------------------------------------------------------------------------------------

int main(int argc, char** argv)
{
try
{
const std::string status_value{"status"};
unsigned long fps_value{0};

const std::vector<std::string> args(argv, argv + argc);
const auto parsed_args{parseArgs(args)};
const std::string fps_filename(std::filesystem::path(args.at(0)).stem().generic_string() + ".saved_fps");

if (args.size() != 2 || (args[1] != status_value && !parseFpsNumber(args[1], fps_value)))
if (!parsed_args)
{
// clang-format off
std::cout << std::endl;
std::cout << " Usage example:" << std::endl;
std::cout << " frltoggle status prints the current FRL value. Value " << FRL_FPS_DISABLED << " means it's disabled." << std::endl;
std::cout << " frltoggle " << FRL_FPS_DISABLED << " turns off the framerate limiter." << std::endl;
std::cout << " frltoggle 60 sets the FPS limit to 60 (allowed values are [" << FRL_FPS_MIN << ", " << FRL_FPS_MAX << "])." << std::endl;
std::cout << " frltoggle status prints the current FRL value. Value " << FRL_FPS_DISABLED << " means it's disabled." << std::endl;
std::cout << " frltoggle " << FRL_FPS_DISABLED << " turns off the framerate limiter." << std::endl;
std::cout << " frltoggle 60 sets the FPS limit to 60 (allowed values are [" << FRL_FPS_MIN << ", " << FRL_FPS_MAX << "])." << std::endl;
std::cout << " frltoggle 60 --save-previous sets the FPS limit to 60 and saves the previous value to a file." << std::endl;
std::cout << " frltoggle 60 --save-previous-or-reuse sets the FPS limit to 60 and saves the previous value to a file." << std::endl <<
" If the file already exists, its value will be validated and reused instead." << std::endl;
std::cout << " frltoggle load-file loads the value from file (e.g., saved using \"--save-previous\") and uses it to set FRL." << std::endl <<
" File is removed afterwards if no errors occurred." << std::endl;
std::cout << std::endl;
// clang-format on
return args.size() < 2 ? EXIT_SUCCESS : EXIT_FAILURE;
Expand All @@ -66,6 +231,12 @@ int main(int argc, char** argv)
NvApiDrsSession drs_session;
NvDRSProfileHandle drs_profile{nullptr};
NVDRS_SETTING drs_setting{};
const auto update_value = [&](unsigned long fps_value)
{
drs_setting.u32CurrentValue = fps_value;
assertSuccess(nvapi.DRS_SetSetting(drs_session, drs_profile, &drs_setting), "Failed to set FRL setting!");
assertSuccess(nvapi.DRS_SaveSettings(drs_session), "Failed to save session settings!");
};

drs_setting.version = NVDRS_SETTING_VER;

Expand All @@ -80,25 +251,57 @@ int main(int argc, char** argv)
throw std::runtime_error("Failed to get FRL setting! Make sure that setting has been saved at least "
"once via NVIDIA Control Panel.");
}
assertSuccess(status, "Failed to set FRL setting!");
assertSuccess(status, "Failed to get FRL setting!");
}

if (args[1] == status_value)
if (std::get_if<StatusOption>(&*parsed_args))
{
std::cout << drs_setting.u32CurrentValue << std::endl;
return EXIT_SUCCESS;
}

drs_setting.u32CurrentValue = fps_value;
if (const auto* option = std::get_if<SetFpsOption>(&*parsed_args))
{
switch (option->m_save)
{
case SetFpsOption::SaveOption::SavePrevious:
{
save_to_file(fps_filename, drs_setting.u32CurrentValue);
break;
}
case SetFpsOption::SaveOption::SavePreviousAndReuse:
{
const auto fps_value{read_fps_from_file(fps_filename)};
if (!fps_value)
{
save_to_file(fps_filename, drs_setting.u32CurrentValue);
}
break;
}
default:
break;
}

update_value(option->m_fps_value);
return EXIT_SUCCESS;
}

if (std::get_if<LoadFileOption>(&*parsed_args))
{
const auto fps_value{read_fps_from_file(fps_filename)};

update_value(*fps_value);
remove_file(fps_filename);

return EXIT_SUCCESS;
}

assertSuccess(nvapi.DRS_SetSetting(drs_session, drs_profile, &drs_setting), "Failed to set FRL setting!");
assertSuccess(nvapi.DRS_SaveSettings(drs_session), "Failed to save session settings!");
throw std::runtime_error("Unhandled code path!");
}
catch (const std::exception& error)
{
std::cerr << error.what() << std::endl;
return EXIT_FAILURE;
}

return EXIT_SUCCESS;
return EXIT_FAILURE;
}

0 comments on commit 7da9b80

Please sign in to comment.