mirror of
https://github.com/optiscaler/OptiScaler.git
synced 2026-09-23 05:46:27 +00:00
Fix timeout in LFX
This commit is contained in:
@@ -1,246 +1,246 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include <dxgi.h>
|
||||
#if _MSC_VER
|
||||
#include <d3d12.h>
|
||||
#else
|
||||
#include "../external/d3d12.h"
|
||||
#endif
|
||||
|
||||
#if _MSC_VER && _WIN64
|
||||
#include "../external/ffx_antilag2_dx12.h"
|
||||
#include "../external/ffx_antilag2_dx11.h"
|
||||
#endif
|
||||
|
||||
#include "../external/latencyflex.h"
|
||||
|
||||
#include "log.h"
|
||||
|
||||
enum Mode {
|
||||
AntiLag2,
|
||||
LatencyFlex,
|
||||
};
|
||||
|
||||
enum CallSpot {
|
||||
SleepCall = 0,
|
||||
InputSample = 1,
|
||||
SimulationStart = 2
|
||||
};
|
||||
|
||||
enum ForceReflex {
|
||||
InGame,
|
||||
ForceDisable,
|
||||
ForceEnable
|
||||
};
|
||||
|
||||
struct LFXStats {
|
||||
uint64_t latency = 0;
|
||||
uint64_t frame_time = 1;
|
||||
uint64_t target = 0;
|
||||
uint64_t frame_id = 0;
|
||||
bool needs_reset = false;
|
||||
};
|
||||
|
||||
class LowLatency {
|
||||
#if _MSC_VER && _WIN64
|
||||
AMD::AntiLag2DX12::Context context_dx12 = {};
|
||||
AMD::AntiLag2DX11::Context context_dx11 = {};
|
||||
Mode mode = AntiLag2;
|
||||
#else
|
||||
Mode mode = LatencyFlex;
|
||||
#endif
|
||||
lfx::LatencyFleX *lf = nullptr;
|
||||
unsigned long min_interval_us = 0;
|
||||
bool al_available = false;
|
||||
bool force_latencyflex = false;
|
||||
bool double_markers = false;
|
||||
ForceReflex force_reflex = InGame;
|
||||
|
||||
static inline uint64_t get_timestamp() {
|
||||
static LARGE_INTEGER frequency = []{
|
||||
LARGE_INTEGER freq;
|
||||
QueryPerformanceFrequency(&freq);
|
||||
return freq;
|
||||
}();
|
||||
|
||||
LARGE_INTEGER counter;
|
||||
QueryPerformanceCounter(&counter);
|
||||
|
||||
return (counter.QuadPart * UINT64_C(1000000000)) / frequency.QuadPart;
|
||||
}
|
||||
|
||||
public:
|
||||
CallSpot call_spot = SimulationStart;
|
||||
LFXStats lfx_stats = {};
|
||||
uint64_t calls_without_sleep = 0;
|
||||
bool fg = false;
|
||||
bool active = true;
|
||||
|
||||
inline void init_al2(IUnknown *pDevice) {
|
||||
#if _MSC_VER && _WIN64
|
||||
if (mode == AntiLag2 && !context_dx12.m_pAntiLagAPI && !context_dx11.m_pAntiLagAPI) {
|
||||
ID3D12Device* device = nullptr;
|
||||
HRESULT hr = pDevice->QueryInterface(__uuidof(ID3D12Device), reinterpret_cast<void**>(&device));
|
||||
if (hr == S_OK) {
|
||||
HRESULT init_return = AMD::AntiLag2DX12::Initialize(&context_dx12, device);
|
||||
if (al_available = init_return == S_OK; !al_available) {
|
||||
mode = LatencyFlex;
|
||||
spdlog::info("AntiLag 2 DX12 initialization failed");
|
||||
} else {
|
||||
spdlog::info("AntiLag 2 DX12 initialized");
|
||||
}
|
||||
} else {
|
||||
HRESULT init_return = AMD::AntiLag2DX11::Initialize(&context_dx11);
|
||||
if (al_available = init_return == S_OK; !al_available) {
|
||||
mode = LatencyFlex;
|
||||
spdlog::info("AntiLag 2 DX11 initialization failed");
|
||||
} else {
|
||||
spdlog::info("AntiLag 2 DX11 initialized");
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
al_available = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void init_lfx() {
|
||||
lf = new lfx::LatencyFleX();
|
||||
force_latencyflex = get_config(L"fakenvapi", L"force_latencyflex", false);
|
||||
force_reflex = (ForceReflex)get_config(L"fakenvapi", L"force_reflex", 0);
|
||||
spdlog::info("LatencyFleX initialized");
|
||||
}
|
||||
|
||||
inline HRESULT update() {
|
||||
if (force_reflex == ForceDisable || (force_reflex == InGame && !active)) return S_FALSE;
|
||||
|
||||
Mode previous_mode = mode;
|
||||
static bool previous_fg_status = fg;
|
||||
|
||||
if (al_available && !force_latencyflex)
|
||||
mode = AntiLag2;
|
||||
else
|
||||
mode = LatencyFlex;
|
||||
|
||||
if (previous_mode != mode) {
|
||||
spdlog::debug("Changed low latency algorithm to: {}", mode == AntiLag2 ? "AntiLag 2" : "LatencyFlex");
|
||||
if (mode == LatencyFlex)
|
||||
lfx_stats.needs_reset = true;
|
||||
}
|
||||
if (previous_fg_status != fg) spdlog::info("FG mode changed to: {}", fg ? "enabled" : "disabled");
|
||||
previous_fg_status = fg;
|
||||
|
||||
spdlog::debug("LowLatency algo: {}", mode == AntiLag2 ? "AntiLag 2" : "LatencyFlex");
|
||||
spdlog::debug("FG status: {}", fg ? "enabled" : "disabled");
|
||||
|
||||
if (mode == AntiLag2) {
|
||||
#if _MSC_VER && _WIN64
|
||||
int max_fps = 0;
|
||||
if (fg && min_interval_us != 0) {
|
||||
mode = LatencyFlex;
|
||||
} else {
|
||||
if (lfx_stats.frame_id != 1) lfx_stats.needs_reset = true;
|
||||
max_fps = min_interval_us > 0 ? 1000000 / min_interval_us : 0;
|
||||
}
|
||||
if (context_dx12.m_pAntiLagAPI)
|
||||
AMD::AntiLag2DX12::Update(&context_dx12, true, max_fps);
|
||||
else if (context_dx11.m_pAntiLagAPI)
|
||||
AMD::AntiLag2DX11::Update(&context_dx11, true, max_fps);
|
||||
#endif
|
||||
}
|
||||
if (mode == LatencyFlex) {
|
||||
if (lfx_stats.needs_reset) {
|
||||
spdlog::info("LFX Reset");
|
||||
lfx_stats.frame_id = 1;
|
||||
lfx_stats.needs_reset = false;
|
||||
lf->Reset();
|
||||
}
|
||||
uint64_t current_timestamp = get_timestamp();
|
||||
uint64_t timestamp;
|
||||
|
||||
// Set FPS Limiter
|
||||
lf->target_frame_time = 1000 * min_interval_us;
|
||||
|
||||
lf->EndFrame(lfx_stats.frame_id, current_timestamp, &lfx_stats.latency, &lfx_stats.frame_time);
|
||||
spdlog::debug("LFX latency: {}, frame_time: {}, current_timestamp: {}", lfx_stats.latency, lfx_stats.frame_time, current_timestamp);
|
||||
lfx_stats.frame_id++;
|
||||
lfx_stats.target = lf->GetWaitTarget(lfx_stats.frame_id);
|
||||
|
||||
if (lfx_stats.target > current_timestamp) {
|
||||
static uint64_t timeout_events = 0;
|
||||
uint64_t timeout_timestamp = current_timestamp + 50000000ULL;
|
||||
if (lfx_stats.target > timeout_timestamp) {
|
||||
timestamp = timeout_timestamp;
|
||||
timeout_events++;
|
||||
lfx_stats.needs_reset = timeout_events > 5;
|
||||
} else {
|
||||
timestamp = lfx_stats.target;
|
||||
timeout_events = 0;
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::nanoseconds(lfx_stats.target - current_timestamp));
|
||||
} else {
|
||||
timestamp = current_timestamp;
|
||||
}
|
||||
|
||||
lf->BeginFrame(lfx_stats.frame_id, lfx_stats.target, timestamp);
|
||||
return S_OK;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
inline HRESULT set_fg_type(bool interpolated) {
|
||||
#if _MSC_VER && _WIN64
|
||||
if (fg)
|
||||
return AMD::AntiLag2DX12::SetFrameGenFrameType(&context_dx12, interpolated);
|
||||
#endif
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
inline HRESULT mark_end_of_rendering() {
|
||||
#if _MSC_VER && _WIN64
|
||||
if (fg)
|
||||
return AMD::AntiLag2DX12::MarkEndOfFrameRendering(&context_dx12);
|
||||
#endif
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
inline void unload() {
|
||||
#if _MSC_VER && _WIN64
|
||||
if (context_dx12.m_pAntiLagAPI && !AMD::AntiLag2DX12::DeInitialize(&context_dx12))
|
||||
spdlog::info("AntiLag 2 DX12 deinitialized");
|
||||
if (context_dx11.m_pAntiLagAPI && !AMD::AntiLag2DX11::DeInitialize(&context_dx11))
|
||||
spdlog::info("AntiLag 2 DX11 deinitialized");
|
||||
#endif
|
||||
}
|
||||
|
||||
void set_min_interval_us(unsigned long interval_us) {
|
||||
if (min_interval_us != interval_us) {
|
||||
min_interval_us = interval_us;
|
||||
spdlog::info("Changed max fps: {}", interval_us > 0 ? 1000000 / interval_us : 0);
|
||||
}
|
||||
}
|
||||
|
||||
Mode get_mode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
bool is_double_markers() {
|
||||
return double_markers;
|
||||
}
|
||||
|
||||
bool ignore_frameid(uint64_t frameid) {
|
||||
constexpr uint64_t allowed_frameid_gap = 64;
|
||||
static uint64_t max_frameid = allowed_frameid_gap;
|
||||
if (bool result = frameid > max_frameid + allowed_frameid_gap; result) {
|
||||
if (!double_markers)
|
||||
spdlog::warn("Double reflex latency markers detected, disable RTSS!");
|
||||
double_markers = true;
|
||||
return result;
|
||||
} else {
|
||||
if (frameid > max_frameid) max_frameid = frameid;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include <dxgi.h>
|
||||
#if _MSC_VER
|
||||
#include <d3d12.h>
|
||||
#else
|
||||
#include "../external/d3d12.h"
|
||||
#endif
|
||||
|
||||
#if _MSC_VER && _WIN64
|
||||
#include "../external/ffx_antilag2_dx12.h"
|
||||
#include "../external/ffx_antilag2_dx11.h"
|
||||
#endif
|
||||
|
||||
#include "../external/latencyflex.h"
|
||||
|
||||
#include "log.h"
|
||||
|
||||
enum Mode {
|
||||
AntiLag2,
|
||||
LatencyFlex,
|
||||
};
|
||||
|
||||
enum CallSpot {
|
||||
SleepCall = 0,
|
||||
InputSample = 1,
|
||||
SimulationStart = 2
|
||||
};
|
||||
|
||||
enum ForceReflex {
|
||||
InGame,
|
||||
ForceDisable,
|
||||
ForceEnable
|
||||
};
|
||||
|
||||
struct LFXStats {
|
||||
uint64_t latency = 0;
|
||||
uint64_t frame_time = 1;
|
||||
uint64_t target = 0;
|
||||
uint64_t frame_id = 0;
|
||||
bool needs_reset = false;
|
||||
};
|
||||
|
||||
class LowLatency {
|
||||
#if _MSC_VER && _WIN64
|
||||
AMD::AntiLag2DX12::Context context_dx12 = {};
|
||||
AMD::AntiLag2DX11::Context context_dx11 = {};
|
||||
Mode mode = AntiLag2;
|
||||
#else
|
||||
Mode mode = LatencyFlex;
|
||||
#endif
|
||||
lfx::LatencyFleX *lf = nullptr;
|
||||
unsigned long min_interval_us = 0;
|
||||
bool al_available = false;
|
||||
bool force_latencyflex = false;
|
||||
bool double_markers = false;
|
||||
ForceReflex force_reflex = InGame;
|
||||
|
||||
static inline uint64_t get_timestamp() {
|
||||
static LARGE_INTEGER frequency = []{
|
||||
LARGE_INTEGER freq;
|
||||
QueryPerformanceFrequency(&freq);
|
||||
return freq;
|
||||
}();
|
||||
|
||||
LARGE_INTEGER counter;
|
||||
QueryPerformanceCounter(&counter);
|
||||
|
||||
return (counter.QuadPart * UINT64_C(1000000000)) / frequency.QuadPart;
|
||||
}
|
||||
|
||||
public:
|
||||
CallSpot call_spot = SimulationStart;
|
||||
LFXStats lfx_stats = {};
|
||||
uint64_t calls_without_sleep = 0;
|
||||
bool fg = false;
|
||||
bool active = true;
|
||||
|
||||
inline void init_al2(IUnknown *pDevice) {
|
||||
#if _MSC_VER && _WIN64
|
||||
if (mode == AntiLag2 && !context_dx12.m_pAntiLagAPI && !context_dx11.m_pAntiLagAPI) {
|
||||
ID3D12Device* device = nullptr;
|
||||
HRESULT hr = pDevice->QueryInterface(__uuidof(ID3D12Device), reinterpret_cast<void**>(&device));
|
||||
if (hr == S_OK) {
|
||||
HRESULT init_return = AMD::AntiLag2DX12::Initialize(&context_dx12, device);
|
||||
if (al_available = init_return == S_OK; !al_available) {
|
||||
mode = LatencyFlex;
|
||||
spdlog::info("AntiLag 2 DX12 initialization failed");
|
||||
} else {
|
||||
spdlog::info("AntiLag 2 DX12 initialized");
|
||||
}
|
||||
} else {
|
||||
HRESULT init_return = AMD::AntiLag2DX11::Initialize(&context_dx11);
|
||||
if (al_available = init_return == S_OK; !al_available) {
|
||||
mode = LatencyFlex;
|
||||
spdlog::info("AntiLag 2 DX11 initialization failed");
|
||||
} else {
|
||||
spdlog::info("AntiLag 2 DX11 initialized");
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
al_available = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void init_lfx() {
|
||||
lf = new lfx::LatencyFleX();
|
||||
force_latencyflex = get_config(L"fakenvapi", L"force_latencyflex", false);
|
||||
force_reflex = (ForceReflex)get_config(L"fakenvapi", L"force_reflex", 0);
|
||||
spdlog::info("LatencyFleX initialized");
|
||||
}
|
||||
|
||||
inline HRESULT update() {
|
||||
if (force_reflex == ForceDisable || (force_reflex == InGame && !active)) return S_FALSE;
|
||||
|
||||
Mode previous_mode = mode;
|
||||
static bool previous_fg_status = fg;
|
||||
|
||||
if (al_available && !force_latencyflex)
|
||||
mode = AntiLag2;
|
||||
else
|
||||
mode = LatencyFlex;
|
||||
|
||||
if (previous_mode != mode) {
|
||||
spdlog::debug("Changed low latency algorithm to: {}", mode == AntiLag2 ? "AntiLag 2" : "LatencyFlex");
|
||||
if (mode == LatencyFlex)
|
||||
lfx_stats.needs_reset = true;
|
||||
}
|
||||
if (previous_fg_status != fg) spdlog::info("FG mode changed to: {}", fg ? "enabled" : "disabled");
|
||||
previous_fg_status = fg;
|
||||
|
||||
spdlog::debug("LowLatency algo: {}", mode == AntiLag2 ? "AntiLag 2" : "LatencyFlex");
|
||||
spdlog::debug("FG status: {}", fg ? "enabled" : "disabled");
|
||||
|
||||
if (mode == AntiLag2) {
|
||||
#if _MSC_VER && _WIN64
|
||||
int max_fps = 0;
|
||||
if (fg && min_interval_us != 0) {
|
||||
mode = LatencyFlex;
|
||||
} else {
|
||||
if (lfx_stats.frame_id != 1) lfx_stats.needs_reset = true;
|
||||
max_fps = min_interval_us > 0 ? 1000000 / min_interval_us : 0;
|
||||
}
|
||||
if (context_dx12.m_pAntiLagAPI)
|
||||
AMD::AntiLag2DX12::Update(&context_dx12, true, max_fps);
|
||||
else if (context_dx11.m_pAntiLagAPI)
|
||||
AMD::AntiLag2DX11::Update(&context_dx11, true, max_fps);
|
||||
#endif
|
||||
}
|
||||
if (mode == LatencyFlex) {
|
||||
if (lfx_stats.needs_reset) {
|
||||
spdlog::info("LFX Reset");
|
||||
lfx_stats.frame_id = 1;
|
||||
lfx_stats.needs_reset = false;
|
||||
lf->Reset();
|
||||
}
|
||||
uint64_t current_timestamp = get_timestamp();
|
||||
uint64_t timestamp;
|
||||
|
||||
// Set FPS Limiter
|
||||
lf->target_frame_time = 1000 * min_interval_us;
|
||||
|
||||
lf->EndFrame(lfx_stats.frame_id, current_timestamp, &lfx_stats.latency, &lfx_stats.frame_time);
|
||||
spdlog::debug("LFX latency: {}, frame_time: {}, current_timestamp: {}", lfx_stats.latency, lfx_stats.frame_time, current_timestamp);
|
||||
lfx_stats.frame_id++;
|
||||
lfx_stats.target = lf->GetWaitTarget(lfx_stats.frame_id);
|
||||
|
||||
if (lfx_stats.target > current_timestamp) {
|
||||
static uint64_t timeout_events = 0;
|
||||
uint64_t timeout_timestamp = current_timestamp + 50000000ULL;
|
||||
if (lfx_stats.target > timeout_timestamp) {
|
||||
timestamp = timeout_timestamp;
|
||||
timeout_events++;
|
||||
lfx_stats.needs_reset = timeout_events > 5;
|
||||
} else {
|
||||
timestamp = lfx_stats.target;
|
||||
timeout_events = 0;
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::nanoseconds(timestamp - current_timestamp));
|
||||
} else {
|
||||
timestamp = current_timestamp;
|
||||
}
|
||||
|
||||
lf->BeginFrame(lfx_stats.frame_id, lfx_stats.target, timestamp);
|
||||
return S_OK;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
inline HRESULT set_fg_type(bool interpolated) {
|
||||
#if _MSC_VER && _WIN64
|
||||
if (fg)
|
||||
return AMD::AntiLag2DX12::SetFrameGenFrameType(&context_dx12, interpolated);
|
||||
#endif
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
inline HRESULT mark_end_of_rendering() {
|
||||
#if _MSC_VER && _WIN64
|
||||
if (fg)
|
||||
return AMD::AntiLag2DX12::MarkEndOfFrameRendering(&context_dx12);
|
||||
#endif
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
inline void unload() {
|
||||
#if _MSC_VER && _WIN64
|
||||
if (context_dx12.m_pAntiLagAPI && !AMD::AntiLag2DX12::DeInitialize(&context_dx12))
|
||||
spdlog::info("AntiLag 2 DX12 deinitialized");
|
||||
if (context_dx11.m_pAntiLagAPI && !AMD::AntiLag2DX11::DeInitialize(&context_dx11))
|
||||
spdlog::info("AntiLag 2 DX11 deinitialized");
|
||||
#endif
|
||||
}
|
||||
|
||||
void set_min_interval_us(unsigned long interval_us) {
|
||||
if (min_interval_us != interval_us) {
|
||||
min_interval_us = interval_us;
|
||||
spdlog::info("Changed max fps: {}", interval_us > 0 ? 1000000 / interval_us : 0);
|
||||
}
|
||||
}
|
||||
|
||||
Mode get_mode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
bool is_double_markers() {
|
||||
return double_markers;
|
||||
}
|
||||
|
||||
bool ignore_frameid(uint64_t frameid) {
|
||||
constexpr uint64_t allowed_frameid_gap = 64;
|
||||
static uint64_t max_frameid = allowed_frameid_gap;
|
||||
if (bool result = frameid > max_frameid + allowed_frameid_gap; result) {
|
||||
if (!double_markers)
|
||||
spdlog::warn("Double reflex latency markers detected, disable RTSS!");
|
||||
double_markers = true;
|
||||
return result;
|
||||
} else {
|
||||
if (frameid > max_frameid) max_frameid = frameid;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user