server/src/framegen_policy.h
Rama main · réplica de lectura del repositorio original.
/**
* @file src/framegen_policy.h
* @brief Stream-start frame generation refresh and limiter policy helpers.
*/
#pragma once
#include <algorithm>
#include <cctype>
#include <cstdint>
#include <limits>
#include <optional>
#include <string>
#include <string_view>
namespace framegen {
struct stream_start_policy_t {
int fps = 0;
// ArtLight Server's raw stream cadence in millihertz when it is available.
int fps_scaled = 0;
// Exact per-client display-mode rate. Zero means use the streamed FPS.
std::uint32_t frame_limit_millihz = 0;
std::string frame_generation_provider {"lossless-scaling"};
std::optional<int> lossless_rtss_limit;
bool smooth_motion = false;
bool capture_fix_enabled = false;
bool uses_virtual_display = false;
bool requires_virtual_display = false;
bool effective_wgc_capture = false;
bool physical_framegen_capture = false;
bool auto_virtual_framegen_limiter = false;
bool frame_generation_enabled = false;
std::optional<int> framegen_refresh_rate;
std::optional<std::uint32_t> framegen_refresh_millihz;
int refresh_multiplier = 1;
// The virtual display runs at framegen_refresh_millihz itself, not a stream multiple,
// so its mode must be created at that rate.
bool fixed_refresh = false;
};
struct stream_start_policy_input_t {
int fps = 0;
int fps_scaled = 0;
std::uint32_t display_refresh_millihz = 0;
bool frame_generation_enabled = false;
bool gen1_framegen_fix = false;
bool gen2_framegen_fix = false;
bool lossless_scaling_framegen = false;
std::optional<int> lossless_rtss_limit;
std::string frame_generation_provider {"lossless-scaling"};
bool uses_virtual_display = false;
std::string capture_mode;
bool auto_capture_uses_wgc = false;
bool auto_virtual_framegen_limiter = true;
int virtual_display_refresh_multiplier = 1;
// Exact virtual-display refresh (e.g. 1000 Hz VRR mode). Non-zero replaces the multiplier.
std::uint32_t virtual_display_fixed_refresh_millihz = 0;
};
inline std::string normalize_provider(std::string_view value) {
std::string normalized;
normalized.reserve(value.size());
for (char ch : value) {
if (std::isalnum(static_cast<unsigned char>(ch))) {
normalized.push_back(static_cast<char>(std::tolower(static_cast<unsigned char>(ch))));
}
}
if (normalized == "nvidia" || normalized == "smoothmotion" || normalized == "nvidiasmoothmotion") {
return "nvidia-smooth-motion";
}
if (normalized == "game" || normalized == "gameprovided" || normalized == "gameprovider") {
return "game-provided";
}
if (normalized == "lossless" || normalized == "losslessscaling") {
return "lossless-scaling";
}
return "lossless-scaling";
}
inline bool virtual_display_reflex_required(
const stream_start_policy_t &policy,
bool allow_virtual_display_override,
bool runtime_sync_override
) {
if (
!policy.uses_virtual_display ||
!policy.auto_virtual_framegen_limiter ||
runtime_sync_override
) {
return false;
}
// A game-provided DLSS/FSR frame-generation stream must keep the low-latency
// virtual-display path even when the global override is enabled. An explicit
// app/client RTSS mode remains the user's strongest opt-in.
if (normalize_provider(policy.frame_generation_provider) == "game-provided") {
return true;
}
return !allow_virtual_display_override;
}
inline std::string normalize_capture_mode(std::string_view value) {
std::string normalized;
normalized.reserve(value.size());
for (char ch : value) {
if (!std::isspace(static_cast<unsigned char>(ch))) {
normalized.push_back(static_cast<char>(std::tolower(static_cast<unsigned char>(ch))));
}
}
return normalized;
}
inline bool provider_implies_frame_generation(std::string_view provider, bool lossless_scaling_framegen) {
const auto normalized = normalize_provider(provider);
if (normalized == "lossless-scaling") {
return lossless_scaling_framegen;
}
return normalized == "game-provided" || normalized == "nvidia-smooth-motion";
}
inline int saturating_refresh_fps(int fps, int multiplier) {
if (fps <= 0 || multiplier <= 1) {
return fps;
}
if (fps > std::numeric_limits<int>::max() / multiplier) {
return std::numeric_limits<int>::max();
}
return fps * multiplier;
}
inline std::uint32_t saturating_refresh_millihz(std::uint32_t millihz, int multiplier) {
if (millihz == 0 || multiplier <= 1) {
return millihz;
}
if (millihz > std::numeric_limits<std::uint32_t>::max() / static_cast<std::uint32_t>(multiplier)) {
return std::numeric_limits<std::uint32_t>::max();
}
return millihz * static_cast<std::uint32_t>(multiplier);
}
inline std::uint32_t normalize_refresh_millihz(int value) {
if (value <= 0) {
return 0;
}
const auto raw = static_cast<std::uint32_t>(value);
return raw >= 1000 ? raw : saturating_refresh_millihz(raw, 1000);
}
inline int rounded_fps_from_millihz(std::uint32_t millihz) {
constexpr std::uint32_t kMillihzPerHertz = 1000;
const auto rounded = (static_cast<std::uint64_t>(millihz) + kMillihzPerHertz / 2) / kMillihzPerHertz;
return rounded > static_cast<std::uint64_t>(std::numeric_limits<int>::max()) ?
std::numeric_limits<int>::max() :
static_cast<int>(rounded);
}
inline stream_start_policy_t make_stream_start_policy(const stream_start_policy_input_t &input) {
stream_start_policy_t policy;
policy.fps = input.fps;
policy.fps_scaled = input.fps_scaled;
policy.frame_limit_millihz = input.display_refresh_millihz;
policy.frame_generation_provider = normalize_provider(input.frame_generation_provider);
policy.lossless_rtss_limit = input.lossless_rtss_limit;
policy.smooth_motion = policy.frame_generation_provider == "nvidia-smooth-motion";
// Legacy capture-fix flags are kept in config for compatibility, but no longer drive
// frame generation policy.
policy.capture_fix_enabled = false;
policy.uses_virtual_display = input.uses_virtual_display;
const bool game_provided_framegen = policy.frame_generation_provider == "game-provided";
const bool provider_enabled = provider_implies_frame_generation(
policy.frame_generation_provider,
input.lossless_scaling_framegen
);
policy.frame_generation_enabled = input.frame_generation_enabled || provider_enabled;
policy.requires_virtual_display = false;
const auto capture_mode = normalize_capture_mode(input.capture_mode);
const bool hard_wgc_capture = policy.uses_virtual_display && game_provided_framegen;
// An explicit DXGI request is a client contract. Game-provided frame
// generation defaults to WGC only when the capture mode is automatic.
const bool explicit_dxgi_capture = capture_mode == "ddx";
const bool explicit_wgc_capture = capture_mode == "wgc" || capture_mode == "wgcc";
const bool auto_wgc_capture = capture_mode.empty() && input.auto_capture_uses_wgc;
policy.effective_wgc_capture =
policy.uses_virtual_display && !explicit_dxgi_capture && (explicit_wgc_capture || auto_wgc_capture || hard_wgc_capture);
policy.physical_framegen_capture = policy.frame_generation_enabled && !policy.uses_virtual_display;
// Virtual displays can apply a matching stream-start frame cap independently from their
// refresh policy. The default automatic policy holds a fixed 4x display refresh; WGC
// changes its latest-frame admission between 2x desktop and 4x game activity. Legacy
// mode instead supplies a fixed 2x multiplier here. VRR mode supplies an exact refresh
// (1000 Hz) that does not depend on the stream rate.
if (policy.uses_virtual_display) {
policy.auto_virtual_framegen_limiter = input.auto_virtual_framegen_limiter;
policy.refresh_multiplier = std::max(1, input.virtual_display_refresh_multiplier);
if (input.virtual_display_fixed_refresh_millihz > 0) {
policy.refresh_multiplier = 1;
policy.fixed_refresh = true;
policy.framegen_refresh_millihz = input.virtual_display_fixed_refresh_millihz;
policy.framegen_refresh_rate = rounded_fps_from_millihz(input.virtual_display_fixed_refresh_millihz);
} else if (policy.refresh_multiplier > 1 && policy.fps > 0) {
const auto base_millihz = input.display_refresh_millihz > 0 ?
input.display_refresh_millihz :
(input.fps_scaled > 0 ?
static_cast<std::uint32_t>(input.fps_scaled) :
normalize_refresh_millihz(policy.fps));
const auto refresh_millihz = saturating_refresh_millihz(base_millihz, policy.refresh_multiplier);
if (refresh_millihz > 0) {
policy.framegen_refresh_millihz = refresh_millihz;
policy.framegen_refresh_rate = rounded_fps_from_millihz(refresh_millihz);
}
}
}
return policy;
}
} // namespace framegen