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);