From cc721bfdaaa997860bc7c9831902f0020194bd26 Mon Sep 17 00:00:00 2001
From: FakeMichau <49685661+FakeMichau@users.noreply.github.com>
Date: Tue, 19 May 2026 02:24:30 +0200
Subject: [PATCH] WIP: Common Low Latency inputs
---
OptiScaler/OptiScaler.vcxproj | 2 +
OptiScaler/OptiScaler.vcxproj.filters | 6 +
OptiScaler/low_latency/input/input_common.cpp | 353 ++++++++++++++++++
OptiScaler/low_latency/input/input_common.h | 102 +++++
OptiScaler/low_latency/ll_util.h | 3 +-
.../low_latency_tech/ll_antilag2.cpp | 4 +-
.../low_latency_tech/ll_antilag2.h | 6 +-
.../low_latency_tech/ll_antilag_vk.cpp | 2 +-
.../low_latency_tech/ll_antilag_vk.h | 6 +-
.../low_latency_tech/ll_latencyflex.cpp | 9 +-
.../low_latency_tech/ll_latencyflex.h | 6 +-
.../low_latency/low_latency_tech/ll_xell.cpp | 55 ++-
.../low_latency/low_latency_tech/ll_xell.h | 21 +-
.../low_latency_tech/low_latency_tech.h | 4 +-
.../nvapi/fakenvapi/low_latency_d3d.cpp | 2 +-
15 files changed, 555 insertions(+), 26 deletions(-)
create mode 100644 OptiScaler/low_latency/input/input_common.cpp
create mode 100644 OptiScaler/low_latency/input/input_common.h
diff --git a/OptiScaler/OptiScaler.vcxproj b/OptiScaler/OptiScaler.vcxproj
index e61e171b..dc98d4d8 100644
--- a/OptiScaler/OptiScaler.vcxproj
+++ b/OptiScaler/OptiScaler.vcxproj
@@ -328,6 +328,7 @@ copy NUL "$(SolutionDir)x64\Release\a\!! EXTRACT ALL FILES TO GAME FOLDER !!" /Y
+
@@ -592,6 +593,7 @@ copy NUL "$(SolutionDir)x64\Release\a\!! EXTRACT ALL FILES TO GAME FOLDER !!" /Y
+
diff --git a/OptiScaler/OptiScaler.vcxproj.filters b/OptiScaler/OptiScaler.vcxproj.filters
index 9bcd1a1b..551c3425 100644
--- a/OptiScaler/OptiScaler.vcxproj.filters
+++ b/OptiScaler/OptiScaler.vcxproj.filters
@@ -872,6 +872,9 @@
Header Files
+
+ Header Files
+
@@ -1355,6 +1358,9 @@
Source Files
+
+ Source Files
+
diff --git a/OptiScaler/low_latency/input/input_common.cpp b/OptiScaler/low_latency/input/input_common.cpp
new file mode 100644
index 00000000..d566dbcd
--- /dev/null
+++ b/OptiScaler/low_latency/input/input_common.cpp
@@ -0,0 +1,353 @@
+#include "pch.h"
+#include "input_common.h"
+#include
+
+bool InputCommon::update_low_latency_tech(IUnknown* pDevice, std::optional mode)
+{
+ LowLatencyMode desiredMode = LowLatencyMode::None;
+
+ if (!pDevice)
+ {
+ // Some outputs might call sleep with nullptr
+ if (activeOutput != LowLatencyMode::None && !mode.has_value())
+ return true; // Allow it if we already have an output and not trying to set manually
+ else
+ return false;
+ }
+
+ if (mode.has_value())
+ desiredMode = mode.value();
+
+ if (desiredMode != LowLatencyMode::None && desiredMode == activeOutput)
+ {
+ // No need to do anything
+ return true;
+ }
+
+ // TODO: init correct currently_active_tech, desiredMode == None -> Auto
+
+ if (auto current_tech = currently_active_tech.load())
+ {
+ activeOutput = current_tech->get_mode();
+ return true;
+ }
+
+ LOG_ERROR("No Low Latency Tech selected");
+ return false;
+}
+
+void InputCommon::add_marker_to_report(MarkerParams* marker_params)
+{
+ 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[marker_params->frame_id % FRAME_REPORTS_BUFFER_SIZE];
+
+ if (current_report->frameID != marker_params->frame_id)
+ {
+ *current_report = FrameReport {};
+ }
+
+ current_report->frameID = marker_params->frame_id;
+ current_report->gpuFrameTimeUs = (uint32_t) (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 (marker_params->marker_type)
+ {
+ case MarkerType::SIMULATION_START:
+ _2nd_last_sim_start = last_sim_start;
+ last_sim_start = get_timestamp() / 1000;
+ current_report->simStartTime = last_sim_start;
+ break;
+ case MarkerType::SIMULATION_END:
+ current_report->simEndTime = get_timestamp() / 1000;
+ break;
+ case MarkerType::RENDERSUBMIT_START:
+ current_report->renderSubmitStartTime = get_timestamp() / 1000;
+ break;
+ case MarkerType::RENDERSUBMIT_END:
+ current_report->renderSubmitEndTime = get_timestamp() / 1000;
+ break;
+ case MarkerType::PRESENT_START:
+ current_report->presentStartTime = get_timestamp() / 1000;
+ break;
+ case MarkerType::PRESENT_END:
+ current_report->presentEndTime = get_timestamp() / 1000;
+ break;
+ case MarkerType::INPUT_SAMPLE:
+ current_report->inputSampleTime = get_timestamp() / 1000;
+ break;
+ default:
+ break;
+ }
+}
+
+InputResult InputCommon::set_low_latency_tech(IUnknown* pDevice, LowLatencyMode mode)
+{
+ if (!update_low_latency_tech(pDevice, mode))
+ return InputResult::LowLatencyUpdateFail;
+
+ return InputResult::Ok;
+}
+
+InputResult InputCommon::sleep(IUnknown* pDevice, const InputContext& inputContext, std::optional frame_id)
+{
+ if (!update_low_latency_tech(pDevice))
+ return InputResult::LowLatencyUpdateFail;
+
+ if (auto current_tech = currently_active_tech.load())
+ current_tech->sleep(frame_id);
+ else
+ return InputResult::GenericError;
+
+ return InputResult::Ok;
+}
+
+InputResult InputCommon::set_marker(const InputContext& inputContext, IUnknown* pDevice, MarkerParams* marker_params)
+{
+ if (!update_low_latency_tech(pDevice))
+ return InputResult::LowLatencyUpdateFail;
+
+ if (!marker_params)
+ return InputResult::InvalidParameter;
+
+ if (inputContext.markerMode == InputMarkerMode::NoMarkers)
+ {
+ return InputResult::InputNotSupported;
+ }
+ else if (inputContext.markerMode == InputMarkerMode::PresentStartOnly &&
+ marker_params->marker_type != MarkerType::PRESENT_START)
+ {
+ return InputResult::InputNotSupported;
+ }
+
+ // update_effective_fg_state();
+
+ // update_enabled_override();
+
+ add_marker_to_report(marker_params);
+
+ if (auto current_tech = currently_active_tech.load())
+ current_tech->set_marker(pDevice, marker_params);
+ else
+ return InputResult::GenericError;
+
+ LOG_TRACE_LOWLATENCY("{}: {}", magic_enum::enum_name(marker_params->marker_type), marker_params->frame_id);
+
+ return InputResult::Ok;
+}
+
+InputResult InputCommon::set_async_marker(const InputContext& inputContext, ID3D12CommandQueue* pCommandQueue,
+ MarkerParams* marker_params)
+{
+ if (!currently_active_tech.load()) // can't init using ID3D12CommandQueue, can only check if available
+ return InputResult::LowLatencyUpdateFail;
+
+ if (!marker_params)
+ return InputResult::InvalidParameter;
+
+ if (inputContext.markerMode == InputMarkerMode::NoMarkers)
+ {
+ return InputResult::InputNotSupported;
+ }
+ else if (inputContext.markerMode == InputMarkerMode::PresentStartOnly &&
+ marker_params->marker_type != MarkerType::OUT_OF_BAND_PRESENT_START)
+ {
+ return InputResult::InputNotSupported;
+ }
+
+ // TODO: could consider adding async markers to the report but would require some rewriting
+ // add_marker_to_report(marker_params);
+
+ if (auto current_tech = currently_active_tech.load())
+ current_tech->set_async_marker(pCommandQueue, marker_params);
+ else
+ return InputResult::GenericError;
+
+ LOG_TRACE_LOWLATENCY("{}: {}", magic_enum::enum_name(marker_params->marker_type), marker_params->frame_id);
+
+ return InputResult::Ok;
+}
+
+// TODO: impl
+InputResult InputCommon::set_sleep_mode(const InputContext& inputContext, IUnknown* pDevice, SleepMode* sleep_mode)
+{
+ return InputResult::Ok;
+}
+
+// TODO: impl
+InputResult InputCommon::get_sleep_status(const InputContext& inputContext, IUnknown* pDevice,
+ SleepParams* sleep_params)
+{
+ return InputResult::Ok;
+}
+
+InputResult InputCommon::get_latency(const InputContext& inputContext, IUnknown* pDev, void* latency_params)
+{
+ if (!latency_params)
+ return InputResult::InvalidParameter;
+
+ if (inputContext.caller == LowLatencyInput::Reflex)
+ {
+ if (activeOutput == LowLatencyMode::Reflex)
+ {
+ // TODO: passthrough call, if success return InputResult::Ok;
+ // return InputResult::Ok;
+ }
+
+ NV_LATENCY_RESULT_PARAMS* reports = (NV_LATENCY_RESULT_PARAMS*) latency_params;
+
+ if (reports->version != NV_LATENCY_RESULT_PARAMS_VER1)
+ {
+ LOG_ERROR("Unsupported version {}", reports->version);
+ return InputResult::InvalidParameter;
+ }
+
+ // Assume no frame reports collected yet, report all zeros
+ if (frame_reports[FRAME_REPORTS_BUFFER_SIZE - 1].frameID == 0)
+ {
+ std::memset(reports->frameReport, 0, sizeof(reports->frameReport));
+ // spdlog::warn("GetLatency: Not enough data to report");
+ return InputResult::GenericError;
+ }
+
+ // Sort frame reports, find the oldest
+ size_t minIdx = 0;
+ uint64_t minID = frame_reports[0].frameID;
+ for (size_t i = 1; i < FRAME_REPORTS_BUFFER_SIZE; i++)
+ {
+ if (frame_reports[i].frameID < minID)
+ {
+ minID = frame_reports[i].frameID;
+ minIdx = i;
+ }
+ }
+
+ // Copy starting from older before wrapping around
+ size_t firstChunk = std::min(NVAPI_BUFFER_SIZE, FRAME_REPORTS_BUFFER_SIZE - minIdx);
+ std::memcpy(reports->frameReport, frame_reports + minIdx, firstChunk * sizeof(FrameReport));
+
+ // Copy the rest after wrapping around
+ if (firstChunk < NVAPI_BUFFER_SIZE)
+ {
+ std::memcpy(reports->frameReport + firstChunk, frame_reports,
+ (NVAPI_BUFFER_SIZE - firstChunk) * sizeof(FrameReport));
+ }
+
+ return InputResult::Ok;
+ }
+ else if (inputContext.caller == LowLatencyInput::XeLL)
+ {
+ if (activeOutput == LowLatencyMode::XeLL)
+ {
+ // TODO: passthrough call, if success return InputResult::Ok;
+ // return InputResult::Ok;
+ }
+
+ xell_frame_report_t* reports = (xell_frame_report_t*) latency_params;
+ constexpr size_t reportCount = 64; // 64 reports, if the app allocated less then it's on them
+
+ // Hopefully not too slow
+ // XeLL doesn't seem to make any guarantees about ordering so no sort needed
+ for (auto i = 0; i < reportCount; i++)
+ {
+ auto& reportOut = reports[i];
+ auto& reportIn = frame_reports[i];
+
+ reportOut.m_frame_id = reportIn.frameID & 0xFFFFFFFF;
+ reportOut.m_sim_start_ts = reportIn.simStartTime;
+ reportOut.m_sim_end_ts = reportIn.simEndTime;
+ reportOut.m_render_submit_start_ts = reportIn.renderSubmitStartTime;
+ reportOut.m_render_submit_end_ts = reportIn.renderSubmitEndTime;
+ reportOut.m_present_start_ts = reportIn.presentStartTime;
+ reportOut.m_present_end_ts = reportIn.presentEndTime;
+ }
+
+ return InputResult::Ok;
+ }
+ else
+ {
+ return InputResult::InputNotSupported;
+ }
+}
+
+xell_result_t InputCommon::pass_xellD3D12SetAppQueue(const InputContext& inputContext, void* appQueue)
+{
+ if (inputContext.caller == LowLatencyInput::XeLL && activeInput == LowLatencyInput::XeLL &&
+ activeOutput == LowLatencyMode::XeLL)
+ {
+ if (auto current_tech = currently_active_tech.load())
+ {
+ auto xell_tech = std::static_pointer_cast(current_tech);
+ return xell_tech->xellD3D12SetAppQueue(appQueue);
+ }
+ }
+
+ return XELL_RESULT_ERROR_UNKNOWN;
+}
+
+xell_result_t InputCommon::pass_xellSetDisplayInfo(const InputContext& inputContext, void* displayInfo)
+{
+ if (inputContext.caller == LowLatencyInput::XeLL && activeInput == LowLatencyInput::XeLL &&
+ activeOutput == LowLatencyMode::XeLL)
+ {
+ if (auto current_tech = currently_active_tech.load())
+ {
+ auto xell_tech = std::static_pointer_cast(current_tech);
+ return xell_tech->xellSetDisplayInfo(displayInfo);
+ }
+ }
+
+ return XELL_RESULT_ERROR_UNKNOWN;
+}
+
+xell_result_t InputCommon::pass_xellSetFgEnabled(const InputContext& inputContext, uint32_t param1, uint32_t param2)
+{
+ if (inputContext.caller == LowLatencyInput::XeLL && activeInput == LowLatencyInput::XeLL &&
+ activeOutput == LowLatencyMode::XeLL)
+ {
+ if (auto current_tech = currently_active_tech.load())
+ {
+ auto xell_tech = std::static_pointer_cast(current_tech);
+ return xell_tech->xellSetFgEnabled(param1, param2);
+ }
+ }
+
+ return XELL_RESULT_ERROR_UNKNOWN;
+}
+
+xell_result_t InputCommon::pass_xellSetGeneratedFramesCount(const InputContext& inputContext, uint32_t param1,
+ uint32_t framesCount)
+{
+ if (inputContext.caller == LowLatencyInput::XeLL && activeInput == LowLatencyInput::XeLL &&
+ activeOutput == LowLatencyMode::XeLL)
+ {
+ if (auto current_tech = currently_active_tech.load())
+ {
+ auto xell_tech = std::static_pointer_cast(current_tech);
+ return xell_tech->xellSetGeneratedFramesCount(param1, framesCount);
+ }
+ }
+
+ return XELL_RESULT_ERROR_UNKNOWN;
+}
+
+xell_result_t InputCommon::pass_xellGetLastPresentStartFrameId(const InputContext& inputContext, uint32_t* p_frame_id)
+{
+ if (inputContext.caller == LowLatencyInput::XeLL && activeInput == LowLatencyInput::XeLL &&
+ activeOutput == LowLatencyMode::XeLL)
+ {
+ if (auto current_tech = currently_active_tech.load())
+ {
+ auto xell_tech = std::static_pointer_cast(current_tech);
+ return xell_tech->xellGetLastPresentStartFrameId(p_frame_id);
+ }
+ }
+
+ return XELL_RESULT_ERROR_UNKNOWN;
+}
diff --git a/OptiScaler/low_latency/input/input_common.h b/OptiScaler/low_latency/input/input_common.h
new file mode 100644
index 00000000..b4ff074d
--- /dev/null
+++ b/OptiScaler/low_latency/input/input_common.h
@@ -0,0 +1,102 @@
+#pragma once
+#include
+#include
+
+enum class LowLatencyInput
+{
+ None,
+ AntiLag2,
+ Reflex,
+ XeLL
+};
+
+#define FRAME_REPORTS_BUFFER_SIZE 70
+#define NVAPI_BUFFER_SIZE 64
+
+struct FrameReport
+{
+ uint64_t frameID;
+ uint64_t inputSampleTime;
+ uint64_t simStartTime;
+ uint64_t simEndTime;
+ uint64_t renderSubmitStartTime;
+ uint64_t renderSubmitEndTime;
+ uint64_t presentStartTime;
+ uint64_t presentEndTime;
+ uint64_t driverStartTime;
+ uint64_t driverEndTime;
+ uint64_t osRenderQueueStartTime;
+ uint64_t osRenderQueueEndTime;
+ uint64_t gpuRenderStartTime;
+ uint64_t gpuRenderEndTime;
+ uint32_t gpuActiveRenderTimeUs;
+ uint32_t gpuFrameTimeUs;
+ uint64_t cameraConstructedTime;
+ uint32_t crossAdapterCopyTimeUs;
+ uint32_t aiFrameTimeUs;
+ uint8_t rsvd[104];
+};
+
+enum class InputMarkerMode
+{
+ NoMarkers,
+ PresentStartOnly,
+ FullMarkers,
+};
+
+struct InputContext
+{
+ LowLatencyInput caller;
+ // bool sleepWithFrameId;
+ bool noFrameId;
+ InputMarkerMode markerMode;
+};
+
+enum class InputResult : uint32_t
+{
+ Ok,
+ InputNotSupported,
+ InvalidParameter,
+ LowLatencyUpdateFail,
+ GenericError,
+};
+
+class InputCommon
+{
+ inline static std::atomic> currently_active_tech;
+ inline static FrameReport frame_reports[FRAME_REPORTS_BUFFER_SIZE] {};
+
+ inline static LowLatencyInput activeInput = LowLatencyInput::None;
+ inline static LowLatencyMode activeOutput = LowLatencyMode::None;
+ inline static bool enabled = false;
+
+ static bool update_low_latency_tech(IUnknown* pDevice, std::optional mode = std::nullopt);
+ static void add_marker_to_report(MarkerParams* marker_params);
+
+ public:
+ static InputResult set_low_latency_tech(IUnknown* pDevice, LowLatencyMode mode);
+
+ // TODO: Ignore all calls that are not coming for the activeInput
+ static InputResult sleep(IUnknown* pDevice, const InputContext& inputContext,
+ std::optional frame_id = std::nullopt);
+ static InputResult set_marker(const InputContext& inputContext, IUnknown* pDevice, MarkerParams* marker_params);
+ static InputResult set_async_marker(const InputContext& inputContext, ID3D12CommandQueue* pCommandQueue,
+ MarkerParams* marker_params);
+ static InputResult set_sleep_mode(const InputContext& inputContext, IUnknown* pDevice, SleepMode* sleep_mode);
+ static InputResult get_sleep_status(const InputContext& inputContext, IUnknown* pDevice, SleepParams* sleep_params);
+ static InputResult
+ get_latency(const InputContext& inputContext, IUnknown* pDev,
+ void* latency_params); // NV_LATENCY_RESULT_PARAMS* for reflex, xell_frame_report_t* for xell,
+ // passthrough when possible, fillout with local frame_reports if not
+
+ // XeLL-specific calls for passthrough, TODO: only allow when caller == activeOutput == activeInput == XeLL
+ static xell_result_t pass_xellD3D12SetAppQueue(
+ const InputContext& inputContext,
+ void* appQueue); // if it's ID3D12CommandQueue* then some translation with async markers can be made
+ static xell_result_t pass_xellSetDisplayInfo(const InputContext& inputContext, void* displayInfo);
+ static xell_result_t pass_xellSetFgEnabled(const InputContext& inputContext, uint32_t param1, uint32_t param2);
+ static xell_result_t pass_xellSetGeneratedFramesCount(const InputContext& inputContext, uint32_t param1,
+ uint32_t framesCount);
+ static xell_result_t pass_xellGetLastPresentStartFrameId(const InputContext& inputContext,
+ uint32_t* p_frame_id); // Maybe not needed
+};
diff --git a/OptiScaler/low_latency/ll_util.h b/OptiScaler/low_latency/ll_util.h
index 0f1ed40e..dd36024a 100644
--- a/OptiScaler/low_latency/ll_util.h
+++ b/OptiScaler/low_latency/ll_util.h
@@ -22,7 +22,8 @@ enum class LowLatencyMode
LatencyFlex,
AntiLag2,
XeLL,
- AntiLagVk
+ AntiLagVk,
+ Reflex
};
void tonvss(NvAPI_ShortString nvss, std::string str);
diff --git a/OptiScaler/low_latency/low_latency_tech/ll_antilag2.cpp b/OptiScaler/low_latency/low_latency_tech/ll_antilag2.cpp
index 3df8f767..8d536abd 100644
--- a/OptiScaler/low_latency/low_latency_tech/ll_antilag2.cpp
+++ b/OptiScaler/low_latency/low_latency_tech/ll_antilag2.cpp
@@ -185,7 +185,7 @@ void AntiLag2::set_sleep_mode(SleepMode* sleep_mode)
sleep_mode->minimum_interval_us; // don't convert to fps due to fg fps limit fallback using intervals
}
-void AntiLag2::sleep()
+void AntiLag2::sleep(std::optional frame_id)
{
last_sleep_framecount = simulation_framecount;
@@ -219,7 +219,7 @@ void AntiLag2::set_marker(IUnknown* pDevice, MarkerParams* marker_params)
}
}
-void AntiLag2::set_async_marker(MarkerParams* marker_params)
+void AntiLag2::set_async_marker(IUnknown* pCommandQueue, MarkerParams* marker_params)
{
if (marker_params->marker_type == MarkerType::OUT_OF_BAND_PRESENT_START)
{
diff --git a/OptiScaler/low_latency/low_latency_tech/ll_antilag2.h b/OptiScaler/low_latency/low_latency_tech/ll_antilag2.h
index 2290c130..819e69c9 100644
--- a/OptiScaler/low_latency/low_latency_tech/ll_antilag2.h
+++ b/OptiScaler/low_latency/low_latency_tech/ll_antilag2.h
@@ -5,7 +5,7 @@
#include
#include
-class AntiLag2 : public virtual LowLatencyTech
+class AntiLag2 : public LowLatencyTech
{
private:
AMD::AntiLag2DX12::Context dx12_ctx = {};
@@ -47,7 +47,7 @@ class AntiLag2 : public virtual LowLatencyTech
void get_sleep_status(SleepParams* sleep_params) override;
void set_sleep_mode(SleepMode* sleep_mode) override;
- void sleep() override;
+ void sleep(std::optional frame_id) override;
void set_marker(IUnknown* pDevice, MarkerParams* marker_params) override;
- void set_async_marker(MarkerParams* marker_params) override;
+ void set_async_marker(IUnknown* pCommandQueue, MarkerParams* marker_params) override;
};
\ No newline at end of file
diff --git a/OptiScaler/low_latency/low_latency_tech/ll_antilag_vk.cpp b/OptiScaler/low_latency/low_latency_tech/ll_antilag_vk.cpp
index e03d1639..98500953 100644
--- a/OptiScaler/low_latency/low_latency_tech/ll_antilag_vk.cpp
+++ b/OptiScaler/low_latency/low_latency_tech/ll_antilag_vk.cpp
@@ -32,7 +32,7 @@ void AntiLagVk::set_sleep_mode(SleepMode* sleep_mode)
sleep_mode->minimum_interval_us > 0 ? (uint32_t) std::round(1000000.0f / sleep_mode->minimum_interval_us) : 0;
}
-void AntiLagVk::sleep()
+void AntiLagVk::sleep(std::optional frame_id)
{
// This can be supported but prefer using markers with frame ids instead
return;
diff --git a/OptiScaler/low_latency/low_latency_tech/ll_antilag_vk.h b/OptiScaler/low_latency/low_latency_tech/ll_antilag_vk.h
index 1515e314..d46ca2dd 100644
--- a/OptiScaler/low_latency/low_latency_tech/ll_antilag_vk.h
+++ b/OptiScaler/low_latency/low_latency_tech/ll_antilag_vk.h
@@ -2,7 +2,7 @@
#include "low_latency_tech.h"
-class AntiLagVk : public virtual LowLatencyTech
+class AntiLagVk : public LowLatencyTech
{
private:
uint32_t max_fps = 0;
@@ -32,7 +32,7 @@ class AntiLagVk : public virtual LowLatencyTech
void get_sleep_status(SleepParams* sleep_params) override;
void set_sleep_mode(SleepMode* sleep_mode) override;
- void sleep() override;
+ void sleep(std::optional frame_id) override;
void set_marker(IUnknown* pDevice, MarkerParams* marker_params) override;
- void set_async_marker(MarkerParams* marker_params) override {}; // Not used by AntiLag VK
+ void set_async_marker(IUnknown* pCommandQueue, MarkerParams* marker_params) override {}; // Not used by AntiLag VK
};
\ No newline at end of file
diff --git a/OptiScaler/low_latency/low_latency_tech/ll_latencyflex.cpp b/OptiScaler/low_latency/low_latency_tech/ll_latencyflex.cpp
index 5882fbe6..af8c320a 100644
--- a/OptiScaler/low_latency/low_latency_tech/ll_latencyflex.cpp
+++ b/OptiScaler/low_latency/low_latency_tech/ll_latencyflex.cpp
@@ -152,14 +152,19 @@ void LatencyFlex::set_sleep_mode(SleepMode* sleep_mode)
minimum_interval_us = sleep_mode->minimum_interval_us;
};
-void LatencyFlex::sleep()
+void LatencyFlex::sleep(std::optional frame_id)
{
if ((LFXMode) Config::Instance()->FN_LatencyFlexMode.value_or_default() != LFXMode::ReflexIDs)
{
last_sleep_framecount = simulation_framecount;
if (current_call_spot == CallSpot::SleepCall)
- lfx_sleep(INVALID_ID);
+ {
+ if (frame_id.has_value())
+ lfx_sleep(frame_id.value());
+ else
+ lfx_sleep(INVALID_ID);
+ }
}
};
diff --git a/OptiScaler/low_latency/low_latency_tech/ll_latencyflex.h b/OptiScaler/low_latency/low_latency_tech/ll_latencyflex.h
index 0aa37358..ce45bdbb 100644
--- a/OptiScaler/low_latency/low_latency_tech/ll_latencyflex.h
+++ b/OptiScaler/low_latency/low_latency_tech/ll_latencyflex.h
@@ -3,7 +3,7 @@
#include "low_latency_tech.h"
#include
-class LatencyFlex : public virtual LowLatencyTech
+class LatencyFlex : public LowLatencyTech
{
private:
lfx::LatencyFleX* ctx = nullptr;
@@ -51,7 +51,7 @@ class LatencyFlex : public virtual LowLatencyTech
void get_sleep_status(SleepParams* sleep_params) override;
void set_sleep_mode(SleepMode* sleep_mode) override;
- void sleep() override;
+ void sleep(std::optional frame_id) override;
void set_marker(IUnknown* pDevice, MarkerParams* marker_params) override;
- void set_async_marker(MarkerParams* marker_params) override {}; // Not used by LFX
+ void set_async_marker(IUnknown* pCommandQueue, MarkerParams* marker_params) override {}; // Not used by LFX
};
\ No newline at end of file
diff --git a/OptiScaler/low_latency/low_latency_tech/ll_xell.cpp b/OptiScaler/low_latency/low_latency_tech/ll_xell.cpp
index d28a7fd2..51d0b9e3 100644
--- a/OptiScaler/low_latency/low_latency_tech/ll_xell.cpp
+++ b/OptiScaler/low_latency/low_latency_tech/ll_xell.cpp
@@ -183,10 +183,55 @@ void XeLL::set_marker(IUnknown* pDevice, MarkerParams* marker_params)
}
}
-void XeLL::sleep()
+void XeLL::sleep(std::optional frame_id)
{
- // This can either be better than sleeping in XELL_SIMULATION_START
- // or be a total mess if +1 is not correct
- sleep_last_id = simulation_start_last_id + 1;
- xell_sleep((uint32_t) sleep_last_id);
+ if (frame_id.has_value())
+ xell_sleep(frame_id.value());
+ else
+ {
+ // This can either be better than sleeping in XELL_SIMULATION_START
+ // or be a total mess if +1 is not correct
+ sleep_last_id = simulation_start_last_id + 1;
+ xell_sleep((uint32_t) sleep_last_id);
+ }
}
+
+xell_result_t XeLL::xellD3D12SetAppQueue(void* appQueue) const
+{
+ if (!o_xellD3D12SetAppQueue)
+ return XELL_RESULT_ERROR_UNKNOWN;
+
+ return o_xellD3D12SetAppQueue(XeLLProxy::Context(), appQueue);
+}
+
+xell_result_t XeLL::xellSetDisplayInfo(void* displayInfo) const
+{
+ if (!o_xellSetDisplayInfo)
+ return XELL_RESULT_ERROR_UNKNOWN;
+
+ return o_xellSetDisplayInfo(XeLLProxy::Context(), displayInfo);
+}
+
+xell_result_t XeLL::xellSetFgEnabled(uint32_t param1, uint32_t param2) const
+{
+ if (!o_xellSetFgEnabled)
+ return XELL_RESULT_ERROR_UNKNOWN;
+
+ return o_xellSetFgEnabled(XeLLProxy::Context(), param1, param2);
+}
+
+xell_result_t XeLL::xellSetGeneratedFramesCount(uint32_t param1, uint32_t framesCount) const
+{
+ if (!o_xellSetGeneratedFramesCount)
+ return XELL_RESULT_ERROR_UNKNOWN;
+
+ return o_xellSetGeneratedFramesCount(XeLLProxy::Context(), param1, framesCount);
+}
+
+xell_result_t XeLL::xellGetLastPresentStartFrameId(uint32_t* p_frame_id) const
+{
+ if (!o_xellGetLastPresentStartFrameId)
+ return XELL_RESULT_ERROR_UNKNOWN;
+
+ return o_xellGetLastPresentStartFrameId(XeLLProxy::Context(), p_frame_id);
+}
\ No newline at end of file
diff --git a/OptiScaler/low_latency/low_latency_tech/ll_xell.h b/OptiScaler/low_latency/low_latency_tech/ll_xell.h
index 74c39a22..2d7b024b 100644
--- a/OptiScaler/low_latency/low_latency_tech/ll_xell.h
+++ b/OptiScaler/low_latency/low_latency_tech/ll_xell.h
@@ -3,13 +3,21 @@
#include "low_latency_tech.h"
#include
+#include "low_latency/input/input_xell.h"
-class XeLL : public virtual LowLatencyTech
+class XeLL : public LowLatencyTech
{
bool sent_sleep_frame_ids[64] {};
uint64_t simulation_start_last_id {};
uint64_t sleep_last_id {};
+ // TODO: set those
+ decltype(&xellD3D12SetAppQueue) o_xellD3D12SetAppQueue = nullptr;
+ decltype(&xellSetDisplayInfo) o_xellSetDisplayInfo = nullptr;
+ decltype(&xellSetFgEnabled) o_xellSetFgEnabled = nullptr;
+ decltype(&xellSetGeneratedFramesCount) o_xellSetGeneratedFramesCount = nullptr;
+ decltype(&xellGetLastPresentStartFrameId) o_xellGetLastPresentStartFrameId = nullptr; // Maybe not needed
+
void xell_sleep(uint32_t frame_id);
void add_marker(uint32_t frame_id, xell_latency_marker_type_t marker);
@@ -38,7 +46,14 @@ class XeLL : public virtual LowLatencyTech
void get_sleep_status(SleepParams* sleep_params) override;
void set_sleep_mode(SleepMode* sleep_mode) override;
- void sleep() override;
+ void sleep(std::optional frame_id) override;
void set_marker(IUnknown* pDevice, MarkerParams* marker_params) override;
- void set_async_marker(MarkerParams* marker_params) override {}; // Not used by XeLL
+ void set_async_marker(IUnknown* pCommandQueue, MarkerParams* marker_params) override {}; // Not used by XeLL
+
+ // For passthrough
+ xell_result_t xellD3D12SetAppQueue(void* appQueue) const;
+ xell_result_t xellSetDisplayInfo(void* displayInfo) const;
+ xell_result_t xellSetFgEnabled(uint32_t param1, uint32_t param2) const;
+ xell_result_t xellSetGeneratedFramesCount(uint32_t param1, uint32_t framesCount) const;
+ xell_result_t xellGetLastPresentStartFrameId(uint32_t* p_frame_id) const;
};
\ No newline at end of file
diff --git a/OptiScaler/low_latency/low_latency_tech/low_latency_tech.h b/OptiScaler/low_latency/low_latency_tech/low_latency_tech.h
index a957454c..f27e5e10 100644
--- a/OptiScaler/low_latency/low_latency_tech/low_latency_tech.h
+++ b/OptiScaler/low_latency/low_latency_tech/low_latency_tech.h
@@ -84,7 +84,7 @@ class LowLatencyTech
virtual void get_sleep_status(SleepParams* sleep_params) = 0;
virtual void set_sleep_mode(SleepMode* sleep_mode) = 0;
- virtual void sleep() = 0;
+ virtual void sleep(std::optional frame_id = std::nullopt) = 0;
virtual void set_marker(IUnknown* pDevice, MarkerParams* marker_params) = 0;
- virtual void set_async_marker(MarkerParams* marker_params) = 0;
+ virtual void set_async_marker(IUnknown* pCommandQueue, MarkerParams* marker_params) = 0;
};
\ No newline at end of file
diff --git a/OptiScaler/nvapi/fakenvapi/low_latency_d3d.cpp b/OptiScaler/nvapi/fakenvapi/low_latency_d3d.cpp
index 428e369f..d92eade9 100644
--- a/OptiScaler/nvapi/fakenvapi/low_latency_d3d.cpp
+++ b/OptiScaler/nvapi/fakenvapi/low_latency_d3d.cpp
@@ -325,7 +325,7 @@ NvAPI_Status LowLatency::SetAsyncFrameMarker(ID3D12CommandQueue* pCommandQueue,
}
if (auto current_tech = currently_active_tech.load())
- current_tech->set_async_marker(&marker_params);
+ current_tech->set_async_marker(pCommandQueue, &marker_params);
LOG_TRACE_FAKENVAPI("Async {}: {}", magic_enum::enum_name(marker_params.marker_type), marker_params.frame_id);