diff --git a/OptiScaler/nvapi/fakenvapi/fakenvapi.cpp b/OptiScaler/nvapi/fakenvapi/fakenvapi.cpp index 6d90dd2b..f7e3b5a7 100644 --- a/OptiScaler/nvapi/fakenvapi/fakenvapi.cpp +++ b/OptiScaler/nvapi/fakenvapi/fakenvapi.cpp @@ -403,7 +403,7 @@ namespace nvd { lowlatency_ctx.init_xell(pDevice); lowlatency_ctx.sleep_called(); - spdlog::debug("LowLatency update called on sleep with result: {}", lowlatency_ctx.update(0)); + spdlog::debug("LowLatency update called on sleep with result: {}", lowlatency_ctx.update(INVALID_ID)); return OK(); } diff --git a/OptiScaler/nvapi/fakenvapi/lowlatency.cpp b/OptiScaler/nvapi/fakenvapi/lowlatency.cpp new file mode 100644 index 00000000..47bdc889 --- /dev/null +++ b/OptiScaler/nvapi/fakenvapi/lowlatency.cpp @@ -0,0 +1,536 @@ +#pragma once + +#include +#if _MSC_VER +#include +#else +#include "../external/d3d12.h" +#endif + +#if _WIN64 +#include "../external/ffx_antilag2_dx12.h" +#include "../external/ffx_antilag2_dx11.h" +#endif + +#include "../external/latencyflex.h" +#include + +#include "log.h" +#include "config.h" +#include "spoof.h" +#include "lowlatency.h" + +// https://learn.microsoft.com/en-us/windows/win32/sync/using-waitable-timer-objects +inline int LowLatency::timer_sleep(int64_t hundred_ns){ + static HANDLE timer = CreateWaitableTimerExW(NULL, NULL, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS); + LARGE_INTEGER due_time; + + due_time.QuadPart = -hundred_ns; + + if(!timer) + return 1; + + if (!SetWaitableTimerEx(timer, &due_time, 0, NULL, NULL, NULL, 0)) + return 2; + + if (WaitForSingleObject(timer, INFINITE) != WAIT_OBJECT_0) + return 3; + + return 0; +}; + +inline int LowLatency::busywait_sleep(int64_t ns) { + auto current_time = get_timestamp(); + auto wait_until = current_time + ns; + while (current_time < wait_until) { + current_time = get_timestamp(); + } + return 0; +} + +inline int LowLatency::eepy(int64_t ns) { + constexpr int64_t busywait_threshold = 2000000; + int status {}; + auto current_time = get_timestamp(); + if (ns <= busywait_threshold) + status = busywait_sleep(ns); + else + status = timer_sleep((ns - busywait_threshold) / 100); + + if (int64_t sleep_deviation = ns - (get_timestamp() - current_time); sleep_deviation > 0 && !status) + status = busywait_sleep(sleep_deviation); + + return status; +} + +void LowLatency::report_marker(NV_LATENCY_MARKER_PARAMS* pSetLatencyMarkerParams) { + auto current_timestamp = get_timestamp() / 1000; + static auto last_sim_start = current_timestamp; + static auto _2nd_last_sim_start = current_timestamp; + auto current_report = &frame_reports[pSetLatencyMarkerParams->frameID % 64]; + current_report->frameID = pSetLatencyMarkerParams->frameID; + current_report->gpuFrameTimeUs = last_sim_start - _2nd_last_sim_start; + current_report->gpuActiveRenderTimeUs = 100; + current_report->driverStartTime = current_timestamp; + current_report->driverEndTime = current_timestamp + 100; + current_report->gpuRenderStartTime = current_timestamp; + current_report->gpuRenderEndTime = current_timestamp + 100; + current_report->osRenderQueueStartTime = current_timestamp; + current_report->osRenderQueueEndTime = current_timestamp + 100; + switch (pSetLatencyMarkerParams->markerType) { + case SIMULATION_START: + _2nd_last_sim_start = last_sim_start; + last_sim_start = get_timestamp() / 1000; + current_report->simStartTime = last_sim_start; + break; + case SIMULATION_END: + current_report->simEndTime = get_timestamp() / 1000; + break; + case RENDERSUBMIT_START: + current_report->renderSubmitStartTime = get_timestamp() / 1000; + break; + case RENDERSUBMIT_END: + current_report->renderSubmitEndTime = get_timestamp() / 1000; + break; + case PRESENT_START: + current_report->presentStartTime = get_timestamp() / 1000; + break; + case PRESENT_END: + current_report->presentEndTime = get_timestamp() / 1000; + break; + case INPUT_SAMPLE: + current_report->inputSampleTime = get_timestamp() / 1000; + break; + default: + break; + } +} + +std::string LowLatency::get_algorithm_name() { + std::string algo; + + switch (mode) { + case Mode::AntiLag2: + algo = "AntiLag 2"; + break; + case Mode::LatencyFlex: + algo = "LatencyFlex"; + break; + case Mode::XeLL: + algo = "XeLL"; + break; + } + + return algo; +} + +void LowLatency::init_al2(IUnknown *pDevice) { +#if _WIN64 + if (mode == Mode::AntiLag2 && !al2_dx12_ctx.m_pAntiLagAPI && !al2_dx11_ctx.m_pAntiLagAPI) { + ID3D12Device* device = nullptr; + HRESULT hr = pDevice->QueryInterface(__uuidof(ID3D12Device), reinterpret_cast(&device)); + if (hr == S_OK) { + // TODO FOR TESTING + HRESULT init_return = AMD::AntiLag2DX12::Initialize(&al2_dx12_ctx, device); + // HRESULT init_return = S_FALSE; + if (al_available = init_return == S_OK; !al_available) { + mode = Mode::XeLL; + spdlog::info("AntiLag 2 DX12 initialization failed"); + } else { + spdlog::info("AntiLag 2 DX12 initialized"); + } + } else { + HRESULT init_return = AMD::AntiLag2DX11::Initialize(&al2_dx11_ctx); + if (al_available = init_return == S_OK; !al_available) { + mode = Mode::XeLL; + spdlog::info("AntiLag 2 DX11 initialization failed"); + } else { + spdlog::info("AntiLag 2 DX11 initialized"); + } + } + } +#else + al_available = false; +#endif +} + +void LowLatency::init_xell(IUnknown* pDevice) { + if (!pDevice || xell_ctx || mode != Mode::XeLL) + return; + + ID3D12Device* dx12_pDevice = nullptr; + HRESULT hr = pDevice->QueryInterface(__uuidof(ID3D12Device), reinterpret_cast(&dx12_pDevice)); + if (hr != S_OK) + return; + + HookIDXGIAdapter1_GetDesc1(); + + spoof(true); + auto result = xellD3D12CreateContext(dx12_pDevice, &xell_ctx); + spoof(false); + + if (result == XELL_RESULT_SUCCESS && xell_ctx) { + mode = Mode::XeLL; + xell_available = true; + xellSetLoggingCallback(xell_ctx, XELL_LOGGING_LEVEL_DEBUG, [](const char* message, xell_logging_level_t loggingLevel) { + switch (loggingLevel) { + case XELL_LOGGING_LEVEL_DEBUG: + spdlog::debug("XeLL: {}", message); + break; + case XELL_LOGGING_LEVEL_INFO: + spdlog::info("XeLL: {}", message); + break; + case XELL_LOGGING_LEVEL_WARNING: + spdlog::warn("XeLL: {}", message); + break; + case XELL_LOGGING_LEVEL_ERROR: + spdlog::error("XeLL: {}", message); + break; + } + }); + } + else { + mode = Mode::LatencyFlex; + } + + spdlog::info("XeLL init result: {}", (int32_t)result); +} + +void LowLatency::init_lfx() { + if (!lfx_ctx) { + lfx_ctx = new lfx::LatencyFleX(); + update_config(); + spdlog::info("LatencyFleX initialized"); + } +} + +inline void LowLatency::update_config() { + force_latencyflex = Config::get().get_force_latencyflex(); + force_reflex = Config::get().get_force_reflex(); + lfx_mode = Config::get().get_latencyflex_mode(); +} + +inline HRESULT LowLatency::update(uint64_t reflex_frame_id) { + std::lock_guard lock(update_mutex); + + update_config(); + + log_event("update", "{}", reflex_frame_id); + if (force_reflex == ForceReflex::ForceDisable || (force_reflex == ForceReflex::InGame && !active)) return S_FALSE; + + bool effective_fg_state = (fg || forced_fg); + Mode previous_mode = mode; + static bool previous_fg_status = effective_fg_state; + static LFXMode previous_lfx_mode = lfx_mode; + + + if (al_available && !force_latencyflex) + mode = Mode::AntiLag2; + else if (xell_available && !force_latencyflex) + mode = Mode::XeLL; + else + mode = Mode::LatencyFlex; + + if (previous_mode != mode) { + spdlog::debug("Changed low latency algorithm to: {}", get_algorithm_name()); + + // Reset XeLL history + for (auto& frame_id : sent_sleep_frame_ids) + frame_id = false; + + if (mode == Mode::LatencyFlex) + lfx_stats.needs_reset = true; + } + + if (previous_fg_status != effective_fg_state) { + spdlog::info("FG mode changed to: {}", effective_fg_state ? "enabled" : "disabled"); + lfx_stats.needs_reset = true; + } + previous_fg_status = effective_fg_state; + + if (previous_lfx_mode != lfx_mode) + lfx_stats.needs_reset = true; + previous_lfx_mode = lfx_mode; + + spdlog::debug("LowLatency algo: {}", get_algorithm_name()); + spdlog::debug("FG status: {}", effective_fg_state ? "enabled" : "disabled"); + + if (mode == Mode::AntiLag2) { +#if _WIN64 + if (lfx_stats.frame_id != 1) lfx_stats.needs_reset = true; + int max_fps = 0; + if ((fg || forced_fg) && min_interval_us != 0) { + static uint64_t previous_frame_time = 0; + uint64_t current_time = get_timestamp(); + uint64_t frame_time = current_time - previous_frame_time; + if (frame_time < 1000 * min_interval_us) { + if (auto res = eepy(min_interval_us * 1000 - frame_time); res) + spdlog::error("Sleep command failed: {}", res); + } + previous_frame_time = get_timestamp(); + } else { + max_fps = min_interval_us > 0 ? std::round(1000000.0f / min_interval_us) : 0; + } + HRESULT result = {}; + auto pre_sleep = get_timestamp(); + if (al2_dx12_ctx.m_pAntiLagAPI) + result = AMD::AntiLag2DX12::Update(&al2_dx12_ctx, true, max_fps); + else if (al2_dx11_ctx.m_pAntiLagAPI) + result = AMD::AntiLag2DX11::Update(&al2_dx11_ctx, true, max_fps); + log_event("al2_sleep", "{}", get_timestamp() - pre_sleep); + return result; +#endif + } else if (mode == Mode::LatencyFlex) { + if (lfx_stats.needs_reset) { + spdlog::info("LFX Reset"); + eepy(200000000ULL); + lfx_stats.frame_id = 1; + lfx_stats.needs_reset = false; + lfx_ctx->Reset(); + } + uint64_t current_timestamp = get_timestamp(); + uint64_t timestamp; + + // Set FPS Limiter + lfx_ctx->target_frame_time = 1000 * min_interval_us; + + if (lfx_mode == LFXMode::Conservative) lfx_end_frame(INVALID_ID); // it should not be using this frame id in the conservative mode + + lfx_mutex.lock(); + auto frame_id = lfx_mode == LFXMode::ReflexIDs ? reflex_frame_id : lfx_stats.frame_id + 1; + log_event("lfx_get_wait_target", "{}", frame_id); + lfx_stats.target = lfx_ctx->GetWaitTarget(frame_id); + lfx_mutex.unlock(); + + 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) { + log_event("lfx_target_high", "{}", 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; + } + log_event("lfx_sleep", "{}", timestamp - current_timestamp); + if (auto res = eepy(timestamp - current_timestamp); res) + spdlog::error("Sleep command failed: {}", res); + } else { + timestamp = current_timestamp; + } + + lfx_mutex.lock(); + lfx_stats.frame_id++; + log_event("lfx_beginframe", "{}", frame_id); + lfx_ctx->BeginFrame(frame_id, lfx_stats.target, timestamp); + lfx_mutex.unlock(); + + return S_OK; + } else if (mode == Mode::XeLL) { + if (reflex_frame_id != INVALID_ID) { // XeLL needs a valid frame_id but NvAPI_D3D_Sleep doesn't provide that + sent_sleep_frame_ids[reflex_frame_id%64] = true; + xellSleep(xell_ctx, reflex_frame_id); + } + + return S_OK; + } + return S_FALSE; +} + +HRESULT LowLatency::set_fg_type(bool interpolated, uint64_t reflex_frame_id) { +#if _WIN64 + if (fg || forced_fg) { + log_event("al2_set_fg_type", "{}", reflex_frame_id); + return AMD::AntiLag2DX12::SetFrameGenFrameType(&al2_dx12_ctx, interpolated); + } +#endif + return S_FALSE; +} + +inline HRESULT LowLatency::mark_end_of_rendering(uint64_t reflex_frame_id) { +#if _WIN64 + if (fg || forced_fg) { + log_event("al2_end_of_rendering", "{}", reflex_frame_id); + return AMD::AntiLag2DX12::MarkEndOfFrameRendering(&al2_dx12_ctx); + } +#endif + return S_FALSE; +} + +inline void LowLatency::lfx_end_frame(uint64_t reflex_frame_id) { + auto current_timestamp = get_timestamp(); + lfx_mutex.lock(); + auto frame_id = lfx_mode == LFXMode::ReflexIDs ? reflex_frame_id : lfx_stats.frame_id; + log_event("lfx_endframe", "{}", frame_id); + lfx_ctx->EndFrame(frame_id, current_timestamp, &lfx_stats.latency, &lfx_stats.frame_time); + lfx_mutex.unlock(); + spdlog::debug("LFX latency: {}, frame_time: {}, current_timestamp: {}", lfx_stats.latency, lfx_stats.frame_time, current_timestamp); +} + +inline void LowLatency::pcl_start(uint64_t reflex_frame_id) { + pcl_start_ids[reflex_frame_id % pcl_max_inprogress_frames] = reflex_frame_id; + pcl_start_timestamps[reflex_frame_id % pcl_max_inprogress_frames] = get_timestamp(); +} + +inline void LowLatency::pcl_end(uint64_t reflex_frame_id) { + if (pcl_start_ids[reflex_frame_id % pcl_max_inprogress_frames] == reflex_frame_id) { + pcl_start_ids[reflex_frame_id % pcl_max_inprogress_frames] = UINT64_MAX; + double time_taken = get_timestamp() - pcl_start_timestamps[reflex_frame_id % pcl_max_inprogress_frames]; + double time_taken_ms = time_taken / 1000000; + log_pcl(time_taken_ms); + } +} + +void LowLatency::xell_set_sleep(NV_SET_SLEEP_MODE_PARAMS* pSetSleepModeParams) { + xell_sleep_params_t sleep_params{}; + sleep_params.minimumIntervalUs = pSetSleepModeParams->minimumIntervalUs; + sleep_params.bLowLatencyMode = pSetSleepModeParams->bLowLatencyMode; + sleep_params.bLowLatencyBoost = pSetSleepModeParams->bLowLatencyBoost; + + xellSetSleepMode(xell_ctx, &sleep_params); +} + +void LowLatency::handle_marker(NV_LATENCY_MARKER_PARAMS* pSetLatencyMarkerParams) { + report_marker(pSetLatencyMarkerParams); + + static std::thread::id simulation_start_thread = {}; + + if (mode == Mode::XeLL + && !sent_sleep_frame_ids[pSetLatencyMarkerParams->frameID%64] + && pSetLatencyMarkerParams->markerType != SIMULATION_START + && pSetLatencyMarkerParams->markerType != INPUT_SAMPLE) + { + spdlog::debug("Skipping reporting a marker for XeLL because sleep wasn't sent for frame id: {}", pSetLatencyMarkerParams->frameID); + return; + } + + switch (pSetLatencyMarkerParams->markerType) { + case SIMULATION_START: + log_event("marker_SIMULATION_START", "{}", pSetLatencyMarkerParams->frameID); + + pcl_start(pSetLatencyMarkerParams->frameID); + + simulation_start_thread = std::this_thread::get_id(); + + if (call_spot == CallSpot::SleepCall) { + calls_without_sleep++; + if (calls_without_sleep > 10) + call_spot = CallSpot::SimulationStart; + } + + if (call_spot != CallSpot::SimulationStart) break; + + spdlog::debug("LowLatency update called on simulation start with result: {}", update(pSetLatencyMarkerParams->frameID)); + + xellAddMarkerData(xell_ctx, pSetLatencyMarkerParams->frameID, XELL_SIMULATION_START); + + break; + case SIMULATION_END: + log_event("marker_SIMULATION_END", "{}", pSetLatencyMarkerParams->frameID); + + xellAddMarkerData(xell_ctx, pSetLatencyMarkerParams->frameID, XELL_SIMULATION_END); + + break; + case RENDERSUBMIT_START: + log_event("marker_RENDERSUBMIT_START", "{}", pSetLatencyMarkerParams->frameID); + + xellAddMarkerData(xell_ctx, pSetLatencyMarkerParams->frameID, XELL_RENDERSUBMIT_START); + + break; + case RENDERSUBMIT_END: + log_event("marker_RENDERSUBMIT_END", "{}", pSetLatencyMarkerParams->frameID); + + xellAddMarkerData(xell_ctx, pSetLatencyMarkerParams->frameID, XELL_RENDERSUBMIT_END); + + if (!fg) + pcl_end(pSetLatencyMarkerParams->frameID); + + if (get_mode() == Mode::LatencyFlex && lfx_mode != LFXMode::Conservative) { + if (std::this_thread::get_id() == simulation_start_thread) { + static bool logged = false; + if (!logged) + spdlog::info("Falling back to LFX Aggressive"); + logged = true; + lfx_mode = LFXMode::Aggressive; + } else { + lfx_end_frame(pSetLatencyMarkerParams->frameID); + } + } + break; + case PRESENT_START: + log_event("marker_PRESENT_START", "{}", pSetLatencyMarkerParams->frameID); + + xellAddMarkerData(xell_ctx, pSetLatencyMarkerParams->frameID, XELL_PRESENT_START); + + mark_end_of_rendering(pSetLatencyMarkerParams->frameID); + + break; + case PRESENT_END: + log_event("marker_PRESENT_END", "{}", pSetLatencyMarkerParams->frameID); + + xellAddMarkerData(xell_ctx, pSetLatencyMarkerParams->frameID, XELL_PRESENT_END); + + break; + case INPUT_SAMPLE: + break; + log_event("marker_INPUT_SAMPLE", "{}", pSetLatencyMarkerParams->frameID); + + if (call_spot == CallSpot::SleepCall && sent_sleep_frame_ids[pSetLatencyMarkerParams->frameID%64]) { + xellAddMarkerData(xell_ctx, pSetLatencyMarkerParams->frameID, XELL_INPUT_SAMPLE); + break; + } + + call_spot = CallSpot::InputSample; + + spdlog::debug("LowLatency update called on input sample with result: {}", update(pSetLatencyMarkerParams->frameID)); + + // in case we are calling xell update from here, sleep needs to happen before any other markers + xellAddMarkerData(xell_ctx, pSetLatencyMarkerParams->frameID, XELL_INPUT_SAMPLE); + + break; + default: + log_event("marker_other", "{}", pSetLatencyMarkerParams->frameID); + break; + } +} + +void LowLatency::sleep_called() { + if (mode != Mode::XeLL) { + call_spot = CallSpot::SleepCall; + calls_without_sleep = 0; + } +} + +void LowLatency::unload() { + spdlog::info("Unloading lowlatency"); +#if _WIN64 + if (al2_dx12_ctx.m_pAntiLagAPI && !AMD::AntiLag2DX12::DeInitialize(&al2_dx12_ctx)) + spdlog::info("AntiLag 2 DX12 deinitialized"); + if (al2_dx11_ctx.m_pAntiLagAPI && !AMD::AntiLag2DX11::DeInitialize(&al2_dx11_ctx)) + spdlog::info("AntiLag 2 DX11 deinitialized"); +#endif + if (lfx_ctx) { + delete lfx_ctx; + lfx_ctx = nullptr; + spdlog::info("LatencyFlex deinitialized"); + } + + if (xell_ctx) { + xellDestroyContext(xell_ctx); + xell_ctx = nullptr; + spdlog::info("XeLL deinitialized"); + } +} + +void LowLatency::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 LowLatency::get_mode() { + return mode; +} \ No newline at end of file diff --git a/OptiScaler/nvapi/fakenvapi/lowlatency.h b/OptiScaler/nvapi/fakenvapi/lowlatency.h index 550d63cf..23084c6c 100644 --- a/OptiScaler/nvapi/fakenvapi/lowlatency.h +++ b/OptiScaler/nvapi/fakenvapi/lowlatency.h @@ -58,6 +58,8 @@ struct FrameReport { NvU8 rsvd[120]; }; +#define INVALID_ID 0xFFFFFFFFFFFFFFFF + class LowLatency { #if _WIN64 Mode mode = Mode::AntiLag2; @@ -79,109 +81,11 @@ class LowLatency { uint64_t pcl_start_timestamps[pcl_max_inprogress_frames] = {}; uint64_t pcl_start_ids[pcl_max_inprogress_frames] = {}; - // https://learn.microsoft.com/en-us/windows/win32/sync/using-waitable-timer-objects - static inline int timer_sleep(int64_t hundred_ns){ - static HANDLE timer = CreateWaitableTimerExW(NULL, NULL, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS); - LARGE_INTEGER due_time; - - due_time.QuadPart = -hundred_ns; - - if(!timer) - return 1; - - if (!SetWaitableTimerEx(timer, &due_time, 0, NULL, NULL, NULL, 0)) - return 2; - - if (WaitForSingleObject(timer, INFINITE) != WAIT_OBJECT_0) - return 3; - - return 0; - }; - - static inline int busywait_sleep(int64_t ns) { - auto current_time = get_timestamp(); - auto wait_until = current_time + ns; - while (current_time < wait_until) { - current_time = get_timestamp(); - } - return 0; - } - - inline int eepy(int64_t ns) { - constexpr int64_t busywait_threshold = 2000000; - int status {}; - auto current_time = get_timestamp(); - if (ns <= busywait_threshold) - status = busywait_sleep(ns); - else - status = timer_sleep((ns - busywait_threshold) / 100); - - if (int64_t sleep_deviation = ns - (get_timestamp() - current_time); sleep_deviation > 0 && !status) - status = busywait_sleep(sleep_deviation); - - return status; - } - - void report_marker(NV_LATENCY_MARKER_PARAMS* pSetLatencyMarkerParams) { - auto current_timestamp = get_timestamp() / 1000; - static auto last_sim_start = current_timestamp; - static auto _2nd_last_sim_start = current_timestamp; - auto current_report = &frame_reports[pSetLatencyMarkerParams->frameID % 64]; - current_report->frameID = pSetLatencyMarkerParams->frameID; - current_report->gpuFrameTimeUs = last_sim_start - _2nd_last_sim_start; - current_report->gpuActiveRenderTimeUs = 100; - current_report->driverStartTime = current_timestamp; - current_report->driverEndTime = current_timestamp + 100; - current_report->gpuRenderStartTime = current_timestamp; - current_report->gpuRenderEndTime = current_timestamp + 100; - current_report->osRenderQueueStartTime = current_timestamp; - current_report->osRenderQueueEndTime = current_timestamp + 100; - switch (pSetLatencyMarkerParams->markerType) { - case SIMULATION_START: - _2nd_last_sim_start = last_sim_start; - last_sim_start = get_timestamp() / 1000; - current_report->simStartTime = last_sim_start; - break; - case SIMULATION_END: - current_report->simEndTime = get_timestamp() / 1000; - break; - case RENDERSUBMIT_START: - current_report->renderSubmitStartTime = get_timestamp() / 1000; - break; - case RENDERSUBMIT_END: - current_report->renderSubmitEndTime = get_timestamp() / 1000; - break; - case PRESENT_START: - current_report->presentStartTime = get_timestamp() / 1000; - break; - case PRESENT_END: - current_report->presentEndTime = get_timestamp() / 1000; - break; - case INPUT_SAMPLE: - current_report->inputSampleTime = get_timestamp() / 1000; - break; - default: - break; - } - } - - std::string get_algorithm_name() { - std::string algo; - - switch (mode) { - case Mode::AntiLag2: - algo = "AntiLag 2"; - break; - case Mode::LatencyFlex: - algo = "LatencyFlex"; - break; - case Mode::XeLL: - algo = "XeLL"; - break; - } - - return algo; - } + static inline int timer_sleep(int64_t hundred_ns); + static inline int busywait_sleep(int64_t ns); + inline int eepy(int64_t ns); + void report_marker(NV_LATENCY_MARKER_PARAMS* pSetLatencyMarkerParams); + std::string get_algorithm_name(); public: #if _WIN64 @@ -193,396 +97,26 @@ public: LFXStats lfx_stats = {}; LFXMode lfx_mode = {}; uint64_t calls_without_sleep = 0; - FrameReport frame_reports[64]; + FrameReport frame_reports[64]{}; + bool sent_sleep_frame_ids[64]{}; bool fg = false; bool forced_fg = false; bool active = true; - inline void init_al2(IUnknown *pDevice) { -#if _WIN64 - if (mode == Mode::AntiLag2 && !al2_dx12_ctx.m_pAntiLagAPI && !al2_dx11_ctx.m_pAntiLagAPI) { - ID3D12Device* device = nullptr; - HRESULT hr = pDevice->QueryInterface(__uuidof(ID3D12Device), reinterpret_cast(&device)); - if (hr == S_OK) { - HRESULT init_return = AMD::AntiLag2DX12::Initialize(&al2_dx12_ctx, device); - if (al_available = init_return == S_OK; !al_available) { - mode = Mode::XeLL; - spdlog::info("AntiLag 2 DX12 initialization failed"); - } else { - spdlog::info("AntiLag 2 DX12 initialized"); - } - } else { - HRESULT init_return = AMD::AntiLag2DX11::Initialize(&al2_dx11_ctx); - if (al_available = init_return == S_OK; !al_available) { - mode = Mode::XeLL; - spdlog::info("AntiLag 2 DX11 initialization failed"); - } else { - spdlog::info("AntiLag 2 DX11 initialized"); - } - } - } -#else - al_available = false; -#endif - } - - inline void init_xell(IUnknown* pDevice) { - if (!pDevice || xell_ctx || mode != Mode::XeLL) - return; - - ID3D12Device* dx12_pDevice = nullptr; - HRESULT hr = pDevice->QueryInterface(__uuidof(ID3D12Device), reinterpret_cast(&dx12_pDevice)); - if (hr != S_OK) - return; - - auto result = xellD3D12CreateContext(dx12_pDevice, &xell_ctx); - - if (result == XELL_RESULT_SUCCESS && xell_ctx) { - mode = Mode::XeLL; - xell_available = true; - xellSetLoggingCallback(xell_ctx, XELL_LOGGING_LEVEL_DEBUG, [](const char* message, xell_logging_level_t loggingLevel) { - switch (loggingLevel) { - case XELL_LOGGING_LEVEL_DEBUG: - spdlog::debug("XeLL: {}", message); - break; - case XELL_LOGGING_LEVEL_INFO: - spdlog::info("XeLL: {}", message); - break; - case XELL_LOGGING_LEVEL_WARNING: - spdlog::warn("XeLL: {}", message); - break; - case XELL_LOGGING_LEVEL_ERROR: - spdlog::error("XeLL: {}", message); - break; - } - }); - } - else { - mode = Mode::LatencyFlex; - } - - spdlog::info("XeLL init result: {}", (int32_t)result); - } - - void init_lfx() { - if (!lfx_ctx) { - lfx_ctx = new lfx::LatencyFleX(); - update_config(); - spdlog::info("LatencyFleX initialized"); - } - } - - inline void update_config() { - force_latencyflex = Config::get().get_force_latencyflex(); - force_reflex = Config::get().get_force_reflex(); - lfx_mode = Config::get().get_latencyflex_mode(); - } - - inline HRESULT update(uint64_t reflex_frame_id) { - std::lock_guard lock(update_mutex); - - update_config(); - - log_event("update", "{}", reflex_frame_id); - if (force_reflex == ForceReflex::ForceDisable || (force_reflex == ForceReflex::InGame && !active)) return S_FALSE; - - bool effective_fg_state = (fg || forced_fg); - Mode previous_mode = mode; - static bool previous_fg_status = effective_fg_state; - static LFXMode previous_lfx_mode = lfx_mode; - - - if (al_available && !force_latencyflex) - mode = Mode::AntiLag2; - else if (xell_available && !force_latencyflex) - mode = Mode::XeLL; - else - mode = Mode::LatencyFlex; - - if (previous_mode != mode) { - spdlog::debug("Changed low latency algorithm to: {}", get_algorithm_name()); - if (mode == Mode::LatencyFlex) - lfx_stats.needs_reset = true; - } - - if (previous_fg_status != effective_fg_state) { - spdlog::info("FG mode changed to: {}", effective_fg_state ? "enabled" : "disabled"); - lfx_stats.needs_reset = true; - } - previous_fg_status = effective_fg_state; - - if (previous_lfx_mode != lfx_mode) - lfx_stats.needs_reset = true; - previous_lfx_mode = lfx_mode; - - spdlog::debug("LowLatency algo: {}", get_algorithm_name()); - spdlog::debug("FG status: {}", effective_fg_state ? "enabled" : "disabled"); - - if (mode == Mode::AntiLag2) { -#if _WIN64 - if (lfx_stats.frame_id != 1) lfx_stats.needs_reset = true; - int max_fps = 0; - if ((fg || forced_fg) && min_interval_us != 0) { - static uint64_t previous_frame_time = 0; - uint64_t current_time = get_timestamp(); - uint64_t frame_time = current_time - previous_frame_time; - if (frame_time < 1000 * min_interval_us) { - if (auto res = eepy(min_interval_us * 1000 - frame_time); res) - spdlog::error("Sleep command failed: {}", res); - } - previous_frame_time = get_timestamp(); - } else { - max_fps = min_interval_us > 0 ? 1000000 / min_interval_us : 0; - } - HRESULT result = {}; - auto pre_sleep = get_timestamp(); - if (al2_dx12_ctx.m_pAntiLagAPI) - result = AMD::AntiLag2DX12::Update(&al2_dx12_ctx, true, max_fps); - else if (al2_dx11_ctx.m_pAntiLagAPI) - result = AMD::AntiLag2DX11::Update(&al2_dx11_ctx, true, max_fps); - log_event("al2_sleep", "{}", get_timestamp() - pre_sleep); - return result; -#endif - } else if (mode == Mode::LatencyFlex) { - if (lfx_stats.needs_reset) { - spdlog::info("LFX Reset"); - eepy(200000000ULL); - lfx_stats.frame_id = 1; - lfx_stats.needs_reset = false; - lfx_ctx->Reset(); - } - uint64_t current_timestamp = get_timestamp(); - uint64_t timestamp; - - // Set FPS Limiter - lfx_ctx->target_frame_time = 1000 * min_interval_us; - - if (lfx_mode == LFXMode::Conservative) lfx_end_frame(0); // it should not be using this frame id in the conservative mode - - lfx_mutex.lock(); - auto frame_id = lfx_mode == LFXMode::ReflexIDs ? reflex_frame_id : lfx_stats.frame_id + 1; - log_event("lfx_get_wait_target", "{}", frame_id); - lfx_stats.target = lfx_ctx->GetWaitTarget(frame_id); - lfx_mutex.unlock(); - - 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) { - log_event("lfx_target_high", "{}", 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; - } - log_event("lfx_sleep", "{}", timestamp - current_timestamp); - if (auto res = eepy(timestamp - current_timestamp); res) - spdlog::error("Sleep command failed: {}", res); - } else { - timestamp = current_timestamp; - } - - lfx_mutex.lock(); - lfx_stats.frame_id++; - log_event("lfx_beginframe", "{}", frame_id); - lfx_ctx->BeginFrame(frame_id, lfx_stats.target, timestamp); - lfx_mutex.unlock(); - - return S_OK; - } else if (mode == Mode::XeLL) { - if (reflex_frame_id > 0) // XeLL needs a valid frame_id but NvAPI_D3D_Sleep doesn't provide that - xellSleep(xell_ctx, reflex_frame_id); - - return S_OK; - } - return S_FALSE; - } - - inline HRESULT set_fg_type(bool interpolated, uint64_t reflex_frame_id) { -#if _WIN64 - if (fg || forced_fg) { - log_event("al2_set_fg_type", "{}", reflex_frame_id); - return AMD::AntiLag2DX12::SetFrameGenFrameType(&al2_dx12_ctx, interpolated); - } -#endif - return S_FALSE; - } - - inline HRESULT mark_end_of_rendering(uint64_t reflex_frame_id) { -#if _WIN64 - if (fg || forced_fg) { - log_event("al2_end_of_rendering", "{}", reflex_frame_id); - return AMD::AntiLag2DX12::MarkEndOfFrameRendering(&al2_dx12_ctx); - } -#endif - return S_FALSE; - } - - inline void lfx_end_frame(uint64_t reflex_frame_id) { - auto current_timestamp = get_timestamp(); - lfx_mutex.lock(); - auto frame_id = lfx_mode == LFXMode::ReflexIDs ? reflex_frame_id : lfx_stats.frame_id; - log_event("lfx_endframe", "{}", frame_id); - lfx_ctx->EndFrame(frame_id, current_timestamp, &lfx_stats.latency, &lfx_stats.frame_time); - lfx_mutex.unlock(); - spdlog::debug("LFX latency: {}, frame_time: {}, current_timestamp: {}", lfx_stats.latency, lfx_stats.frame_time, current_timestamp); - } - - inline void pcl_start(uint64_t reflex_frame_id) { - pcl_start_ids[reflex_frame_id % pcl_max_inprogress_frames] = reflex_frame_id; - pcl_start_timestamps[reflex_frame_id % pcl_max_inprogress_frames] = get_timestamp(); - } - - inline void pcl_end(uint64_t reflex_frame_id) { - if (pcl_start_ids[reflex_frame_id % pcl_max_inprogress_frames] == reflex_frame_id) { - pcl_start_ids[reflex_frame_id % pcl_max_inprogress_frames] = UINT64_MAX; - double time_taken = get_timestamp() - pcl_start_timestamps[reflex_frame_id % pcl_max_inprogress_frames]; - double time_taken_ms = time_taken / 1000000; - log_pcl(time_taken_ms); - } - } - - void xell_set_sleep(NV_SET_SLEEP_MODE_PARAMS* pSetSleepModeParams) { - xell_sleep_params_t sleep_params{}; - sleep_params.minimumIntervalUs = pSetSleepModeParams->minimumIntervalUs; - sleep_params.bLowLatencyMode = pSetSleepModeParams->bLowLatencyMode; - sleep_params.bLowLatencyBoost = pSetSleepModeParams->bLowLatencyBoost; - - xellSetSleepMode(xell_ctx, &sleep_params); - } - - void handle_marker(NV_LATENCY_MARKER_PARAMS* pSetLatencyMarkerParams) { - report_marker(pSetLatencyMarkerParams); - - static std::thread::id simulation_start_thread = {}; - - switch (pSetLatencyMarkerParams->markerType) { - case SIMULATION_START: - log_event("marker_SIMULATION_START", "{}", pSetLatencyMarkerParams->frameID); - - pcl_start(pSetLatencyMarkerParams->frameID); - - simulation_start_thread = std::this_thread::get_id(); - - if (call_spot == CallSpot::SleepCall) { - calls_without_sleep++; - if (calls_without_sleep > 10) - call_spot = CallSpot::SimulationStart; - } - - if (call_spot != CallSpot::SimulationStart) break; - - spdlog::debug("LowLatency update called on simulation start with result: {}", update(pSetLatencyMarkerParams->frameID)); - - xellAddMarkerData(xell_ctx, pSetLatencyMarkerParams->frameID, XELL_SIMULATION_START); - - break; - case SIMULATION_END: - log_event("marker_SIMULATION_END", "{}", pSetLatencyMarkerParams->frameID); - - xellAddMarkerData(xell_ctx, pSetLatencyMarkerParams->frameID, XELL_SIMULATION_END); - - break; - case RENDERSUBMIT_START: - log_event("marker_RENDERSUBMIT_START", "{}", pSetLatencyMarkerParams->frameID); - - xellAddMarkerData(xell_ctx, pSetLatencyMarkerParams->frameID, XELL_RENDERSUBMIT_START); - - break; - case RENDERSUBMIT_END: - log_event("marker_RENDERSUBMIT_END", "{}", pSetLatencyMarkerParams->frameID); - - xellAddMarkerData(xell_ctx, pSetLatencyMarkerParams->frameID, XELL_RENDERSUBMIT_END); - - if (!fg) - pcl_end(pSetLatencyMarkerParams->frameID); - - if (get_mode() == Mode::LatencyFlex && lfx_mode != LFXMode::Conservative) { - if (std::this_thread::get_id() == simulation_start_thread) { - static bool logged = false; - if (!logged) - spdlog::info("Falling back to LFX Aggressive"); - logged = true; - lfx_mode = LFXMode::Aggressive; - } else { - lfx_end_frame(pSetLatencyMarkerParams->frameID); - } - } - break; - case PRESENT_START: - log_event("marker_PRESENT_START", "{}", pSetLatencyMarkerParams->frameID); - - xellAddMarkerData(xell_ctx, pSetLatencyMarkerParams->frameID, XELL_PRESENT_START); - - mark_end_of_rendering(pSetLatencyMarkerParams->frameID); - - break; - case PRESENT_END: - log_event("marker_PRESENT_END", "{}", pSetLatencyMarkerParams->frameID); - - xellAddMarkerData(xell_ctx, pSetLatencyMarkerParams->frameID, XELL_PRESENT_END); - - break; - case INPUT_SAMPLE: - log_event("marker_INPUT_SAMPLE", "{}", pSetLatencyMarkerParams->frameID); - - if (call_spot == CallSpot::SleepCall) { - xellAddMarkerData(xell_ctx, pSetLatencyMarkerParams->frameID, XELL_INPUT_SAMPLE); - break; - } - - call_spot = CallSpot::InputSample; - - spdlog::debug("LowLatency update called on input sample with result: {}", update(pSetLatencyMarkerParams->frameID)); - - // in case we are calling xell update from here, sleep needs to happen before any other markers - xellAddMarkerData(xell_ctx, pSetLatencyMarkerParams->frameID, XELL_INPUT_SAMPLE); - - break; - default: - log_event("marker_other", "{}", pSetLatencyMarkerParams->frameID); - break; - } - } - - void sleep_called() { - if (mode != Mode::XeLL) { - call_spot = CallSpot::SleepCall; - calls_without_sleep = 0; - } - } - - inline void unload() { - spdlog::info("Unloading lowlatency"); -#if _WIN64 - if (al2_dx12_ctx.m_pAntiLagAPI && !AMD::AntiLag2DX12::DeInitialize(&al2_dx12_ctx)) - spdlog::info("AntiLag 2 DX12 deinitialized"); - if (al2_dx11_ctx.m_pAntiLagAPI && !AMD::AntiLag2DX11::DeInitialize(&al2_dx11_ctx)) - spdlog::info("AntiLag 2 DX11 deinitialized"); -#endif - if (lfx_ctx) { - delete lfx_ctx; - lfx_ctx = nullptr; - spdlog::info("LatencyFlex deinitialized"); - } - - if (xell_ctx) { - xellDestroyContext(xell_ctx); - xell_ctx = nullptr; - spdlog::info("XeLL deinitialized"); - } - } - - 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; - } -}; \ No newline at end of file + void init_al2(IUnknown *pDevice); + void init_xell(IUnknown* pDevice); + void init_lfx(); + inline void update_config(); + inline HRESULT update(uint64_t reflex_frame_id) ; + HRESULT set_fg_type(bool interpolated, uint64_t reflex_frame_id); + inline HRESULT mark_end_of_rendering(uint64_t reflex_frame_id); + inline void lfx_end_frame(uint64_t reflex_frame_id); + inline void pcl_start(uint64_t reflex_frame_id); + inline void pcl_end(uint64_t reflex_frame_id); + void xell_set_sleep(NV_SET_SLEEP_MODE_PARAMS* pSetSleepModeParams); + void handle_marker(NV_LATENCY_MARKER_PARAMS* pSetLatencyMarkerParams); + void sleep_called(); + void unload(); + void set_min_interval_us(unsigned long interval_us); + Mode get_mode(); +}; diff --git a/OptiScaler/nvapi/fakenvapi/meson.build b/OptiScaler/nvapi/fakenvapi/meson.build index b45b6dc9..5e546bb6 100644 --- a/OptiScaler/nvapi/fakenvapi/meson.build +++ b/OptiScaler/nvapi/fakenvapi/meson.build @@ -1,6 +1,6 @@ dll = shared_library( 'nvapi'+target_suffix, - ['main.cpp', 'fakenvapi.cpp', 'util.cpp', 'log.cpp', fakenvapi_version], + ['main.cpp', 'fakenvapi.cpp', 'lowlatency.cpp', 'util.cpp', 'log.cpp', fakenvapi_version], rc, name_prefix : '', dependencies : [ lib_dxgi, lib_xell ], diff --git a/OptiScaler/nvapi/fakenvapi/spoof.h b/OptiScaler/nvapi/fakenvapi/spoof.h new file mode 100644 index 00000000..587ec1a9 --- /dev/null +++ b/OptiScaler/nvapi/fakenvapi/spoof.h @@ -0,0 +1,62 @@ +#pragma once + +#include +#include +#include +#include + +typedef HRESULT(STDMETHODCALLTYPE* GetDesc1Func)(IDXGIAdapter1*, DXGI_ADAPTER_DESC1*); +GetDesc1Func g_originalGetDesc1 = nullptr; +static bool spoof_intel = false; + +// Our hook +HRESULT STDMETHODCALLTYPE MyGetDesc1Hook(IDXGIAdapter1* pThis, DXGI_ADAPTER_DESC1* pDesc) { + std::cout << "IDXGIAdapter1::GetDesc1 hooked!" << std::endl; + + // Call the original + HRESULT hr = g_originalGetDesc1(pThis, pDesc); + + // Modify the description as an example + if (SUCCEEDED(hr) && spoof_intel) { + pDesc->VendorId = 0x8086; + pDesc->DeviceId = 0x56A0; + + std::wstring szName = L"Intel(R) Arc(TM) A770 Graphics"; + std::memset(pDesc->Description, 0, sizeof(pDesc->Description)); + std::wcscpy(pDesc->Description, szName.c_str()); + } + + return hr; +} + +void HookIDXGIAdapter1_GetDesc1() { + if (g_originalGetDesc1) + return; + + IDXGIFactory1* pFactory = nullptr; + if (FAILED(CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&pFactory))) + return; + + IDXGIAdapter1* pAdapter = nullptr; + if (FAILED(pFactory->EnumAdapters1(0, &pAdapter))) { + pFactory->Release(); + return; + } + + void** vtable = *(void***)pAdapter; + + DWORD oldProtect; + VirtualProtect(&vtable[10], sizeof(void*), PAGE_EXECUTE_READWRITE, &oldProtect); + + g_originalGetDesc1 = (GetDesc1Func)vtable[10]; // Index 10 is GetDesc1 in IDXGIAdapter1 vtable + vtable[10] = (void*)&MyGetDesc1Hook; + + VirtualProtect(&vtable[10], sizeof(void*), oldProtect, &oldProtect); + + pAdapter->Release(); + pFactory->Release(); +} + +void spoof(bool toggle) { + spoof_intel = toggle; +} \ No newline at end of file