mirror of
https://github.com/optiscaler/OptiScaler.git
synced 2026-09-03 12:05:34 +00:00
XeLL Spoof
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,536 @@
|
||||
#pragma once
|
||||
|
||||
#include <dxgi.h>
|
||||
#if _MSC_VER
|
||||
#include <d3d12.h>
|
||||
#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 <xell_d3d12.h>
|
||||
|
||||
#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<void**>(&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<void**>(&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<std::mutex> 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;
|
||||
}
|
||||
@@ -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<void**>(&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<void**>(&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<std::mutex> 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;
|
||||
}
|
||||
};
|
||||
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();
|
||||
};
|
||||
|
||||
@@ -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 ],
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
#include <dxgi.h>
|
||||
#include <dxgi1_2.h>
|
||||
#include <windows.h>
|
||||
#include <iostream>
|
||||
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user