Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions src/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,14 @@ namespace config {
true, // nv_realtime_hags
true, // nv_opengl_vulkan_on_dxgi
true, // nv_sunshine_high_power_mode
false, // nv_optimize_game (opt-in)
true, // nv_force_vsync
true, // nv_lock_frame_rate
-2, // nv_frl_fps_offset
0, // nv_frl_fps_override (0 = derive from client fps)
false, // nv_prefer_max_performance
false, // nv_low_latency_mode
false, // nv_apply_to_base_profile (opt-in, machine-wide)
false, // vdd_keep_enabled
false, // vdd_headless_create_enabled
false, // vdd_reuse (default: recreate VDD for each client)
Expand Down Expand Up @@ -1127,6 +1135,26 @@ namespace config {
bool_f(vars, "nvenc_opengl_vulkan_on_dxgi", video.nv_opengl_vulkan_on_dxgi);
bool_f(vars, "nvenc_latency_over_power", video.nv_sunshine_high_power_mode);

bool_f(vars, "nvenc_optimize_game", video.nv_optimize_game);
bool_f(vars, "nvenc_force_vsync", video.nv_force_vsync);
bool_f(vars, "nvenc_lock_frame_rate", video.nv_lock_frame_rate);
int_f(vars, "nvenc_frl_fps_offset", video.nv_frl_fps_offset);
int_f(vars, "nvenc_frl_fps_override", video.nv_frl_fps_override);
// Clamp to the same ranges enforced in the Web UI to defend against
// tampered config files (avoids signed overflow when later combined
// with the client framerate, and matches what the slider permits).
if (video.nv_frl_fps_offset < -30 || video.nv_frl_fps_offset > 30) {
BOOST_LOG(warning) << "nvenc_frl_fps_offset out of range [-30, 30]; clamping from " << video.nv_frl_fps_offset;
video.nv_frl_fps_offset = std::clamp(video.nv_frl_fps_offset, -30, 30);
}
if (video.nv_frl_fps_override < 0 || video.nv_frl_fps_override > 500) {
BOOST_LOG(warning) << "nvenc_frl_fps_override out of range [0, 500]; clamping from " << video.nv_frl_fps_override;
video.nv_frl_fps_override = std::clamp(video.nv_frl_fps_override, 0, 500);
}
bool_f(vars, "nvenc_prefer_max_performance", video.nv_prefer_max_performance);
bool_f(vars, "nvenc_low_latency_mode", video.nv_low_latency_mode);
bool_f(vars, "nvenc_apply_to_base_profile", video.nv_apply_to_base_profile);

#if !defined(__ANDROID__) && !defined(__APPLE__)
video.nv_legacy.preset = video.nv.quality_preset + 11;
video.nv_legacy.multipass = video.nv.two_pass == nvenc::nvenc_two_pass::quarter_resolution ? NV_ENC_TWO_PASS_QUARTER_RESOLUTION :
Expand Down
14 changes: 14 additions & 0 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,20 @@ namespace config {
bool nv_realtime_hags;
bool nv_opengl_vulkan_on_dxgi;
bool nv_sunshine_high_power_mode;

// Stream-time NVIDIA driver optimizations applied via NvAPI to the game's
// application profile (and optionally the BASE / global profile). All
// changes are persisted to an undo manifest in %ProgramData%\Sunshine and
// rolled back when the stream stops, or on the next launch after a crash.
bool nv_optimize_game; // master switch for the per-game optimizations
bool nv_force_vsync; // VSYNCMODE -> FORCEON
bool nv_lock_frame_rate; // FRL_FPS -> client_fps + nv_frl_fps_offset
int nv_frl_fps_offset; // delta added to client fps for FRL target
int nv_frl_fps_override; // if > 0, use this value directly
bool nv_prefer_max_performance; // PREFERRED_PSTATE -> PREFER_MAX
bool nv_low_latency_mode; // PRERENDERLIMIT -> 1 (NVCP "Low Latency Mode = On")
bool nv_apply_to_base_profile; // also write the same settings to the BASE profile

bool vdd_keep_enabled;
/** When true, after stream end if no display is found (headless), create Zako VDD automatically. Default false. */
bool vdd_headless_create_enabled;
Expand Down
22 changes: 22 additions & 0 deletions src/platform/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,28 @@ namespace platf {
void
streaming_will_stop();

/**
* @brief Apply per-stream GPU driver optimizations for the launched game.
* On Windows this writes NVIDIA application/BASE profile entries
* (force VSync, FRL frame-rate cap, low latency, P-state) gated on
* the `nv_optimize_game` config switch and on the system actually
* being NVIDIA. On other platforms this is a no-op.
* @param game_cmd The command line of the running game (e.g. proc::proc
* .get_app_cmd()). Used to derive the EXE basename for
* the per-game profile leg. Empty string is allowed and
* disables only the per-game leg.
* @param client_fps Client refresh rate, used to derive the FRL target.
*/
void
apply_stream_optimizations(const std::string &game_cmd, int client_fps);

/**
* @brief Roll back any changes made by apply_stream_optimizations().
* Safe to call unconditionally; does nothing if nothing was applied.
*/
void
restore_stream_optimizations();

/**
* @brief Enter Away Mode - display turns off, system stays running for instant wake.
* On Windows, this uses ES_AWAYMODE_REQUIRED + ES_SYSTEM_REQUIRED and turns off the monitor.
Expand Down
10 changes: 10 additions & 0 deletions src/platform/linux/misc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,16 @@ namespace platf {
// Nothing to do
}

void
apply_stream_optimizations(const std::string &, int) {
// Nothing to do (Linux has no NVAPI driver-profile equivalent here yet)
}

void
restore_stream_optimizations() {
// Nothing to do
}

void
enter_away_mode() {
// TODO: Linux implementation could use DPMS to turn off display
Expand Down
10 changes: 10 additions & 0 deletions src/platform/macos/misc.mm
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,16 @@
// Nothing to do
}

void
apply_stream_optimizations(const std::string &, int) {
// Nothing to do on macOS
}

void
restore_stream_optimizations() {
// Nothing to do
}

void
enter_away_mode() {
BOOST_LOG(info) << "Away Mode is not yet implemented on macOS"sv;
Expand Down
61 changes: 61 additions & 0 deletions src/platform/windows/misc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@

#include "misc.h"

#include "src/config.h"
#include "src/entry_handler.h"
#include "src/globals.h"
#include "src/logging.h"
Expand Down Expand Up @@ -1401,6 +1402,66 @@ namespace platf {
}
}

namespace {
// Forward declaration; defined later in this TU.
std::wstring from_utf8(const std::string &string);

/**
* @brief Best-effort lower-case wide basename of a launch command line.
* e.g. "\"C:\\Games\\foo\\bar.exe\" --opt" -> L"bar.exe"
*/
std::wstring
extract_exe_basename_w(const std::string &cmd) {
if (cmd.empty()) return {};
std::string token;
if (cmd.front() == '"') {
auto end = cmd.find('"', 1);
token = cmd.substr(1, end == std::string::npos ? std::string::npos : end - 1);
}
else {
auto end = cmd.find_first_of(" \t");
token = cmd.substr(0, end);
}
if (token.empty()) return {};
// Convert UTF-8 to UTF-16 first so that std::filesystem::path doesn't
// misinterpret the bytes via the legacy ANSI code page on Windows
// (breaks profile lookup for non-ASCII paths).
auto wide_token = from_utf8(token);
if (wide_token.empty()) return {};
auto fname = std::filesystem::path(wide_token).filename().wstring();
std::transform(fname.begin(), fname.end(), fname.begin(), [](wchar_t c) {
return static_cast<wchar_t>(::towlower(c));
});
return fname;
}
} // namespace

void
apply_stream_optimizations(const std::string &game_cmd, int client_fps) {
if (!config::video.nv_optimize_game) {
return;
}
auto exe_w = extract_exe_basename_w(game_cmd);
if (!nvprefs_instance.load()) {
// Either non-NV system or driver init failed -- silently skip.
return;
}
nvprefs_instance.apply_stream_optimizations(exe_w, client_fps);
nvprefs_instance.unload();
}

void
restore_stream_optimizations() {
if (!config::video.nv_optimize_game) {
return;
}
if (!nvprefs_instance.load()) {
return;
}
nvprefs_instance.restore_stream_optimizations();
nvprefs_instance.unload();
}

void
enter_away_mode() {
if (away_mode_active.exchange(true)) {
Expand Down
Loading
Loading