From 255d3bf7a4e4cf71bca3d18c97d8e2b491eca29f Mon Sep 17 00:00:00 2001 From: FakeMichau <49685661+FakeMichau@users.noreply.github.com> Date: Mon, 9 Jun 2025 01:47:40 +0200 Subject: [PATCH] Add LFX to Vulkan --- OptiScaler/nvapi/fakenvapi/fakenvapi.cpp | 44 +++-- OptiScaler/nvapi/fakenvapi/low_latency.cpp | 207 ++++++++++++++++++++- OptiScaler/nvapi/fakenvapi/low_latency.h | 17 +- 3 files changed, 250 insertions(+), 18 deletions(-) diff --git a/OptiScaler/nvapi/fakenvapi/fakenvapi.cpp b/OptiScaler/nvapi/fakenvapi/fakenvapi.cpp index e46e470b..fa59e856 100644 --- a/OptiScaler/nvapi/fakenvapi/fakenvapi.cpp +++ b/OptiScaler/nvapi/fakenvapi/fakenvapi.cpp @@ -327,8 +327,7 @@ namespace nvd { } NvAPI_Status __cdecl NvAPI_D3D_GetSleepStatus(IUnknown* pDevice, NV_GET_SLEEP_STATUS_PARAMS* pGetSleepStatusParams) { - lowlatency_ctx.GetSleepStatus(pDevice, pGetSleepStatusParams); - return OK(); + return lowlatency_ctx.GetSleepStatus(pDevice, pGetSleepStatusParams); } NvAPI_Status __cdecl NvAPI_D3D_GetLatency(IUnknown* pDev, NV_LATENCY_RESULT_PARAMS* pGetLatencyParams) { @@ -339,9 +338,7 @@ namespace nvd { return ERROR_VALUE(NVAPI_INVALID_ARGUMENT); // xellGetFramesReports() currently doesn't give any extra data that we can't already get - lowlatency_ctx.get_latency_result(pGetLatencyParams); - - return OK(); + return lowlatency_ctx.GetLatency(pDev, pGetLatencyParams); } NvAPI_Status __cdecl NvAPI_D3D_SetSleepMode(IUnknown* pDevice, NV_SET_SLEEP_MODE_PARAMS* pSetSleepModeParams) { @@ -546,37 +543,58 @@ namespace nvd { if (pSetAsyncFrameMarkerParams == nullptr) return ERROR_VALUE(NVAPI_INVALID_ARGUMENT); - lowlatency_ctx.SetAsyncFrameMarker(pCommandQueue, pSetAsyncFrameMarkerParams); - - return OK(); + return lowlatency_ctx.SetAsyncFrameMarker(pCommandQueue, pSetAsyncFrameMarkerParams); } NvAPI_Status __cdecl NvAPI_Vulkan_InitLowLatencyDevice(__in HANDLE vkDevice, __out HANDLE *signalSemaphoreHandle) { + // It happens automatically on every reflex call return OK(); } NvAPI_Status __cdecl NvAPI_Vulkan_DestroyLowLatencyDevice(__in HANDLE vkDevice) { + lowlatency_ctx.deinit_current_tech(); return OK(); } NvAPI_Status __cdecl NvAPI_Vulkan_GetSleepStatus(__in HANDLE vkDevice, __inout NV_VULKAN_GET_SLEEP_STATUS_PARAMS *pGetSleepStatusParams) { - return OK(); + return lowlatency_ctx.GetSleepStatus(vkDevice, pGetSleepStatusParams); } NvAPI_Status __cdecl NvAPI_Vulkan_SetSleepMode(__in HANDLE vkDevice, __in NV_VULKAN_SET_SLEEP_MODE_PARAMS *pSetSleepModeParams) { - return OK(); + if (!Init()) + return ERROR(); + + return lowlatency_ctx.SetSleepMode(vkDevice, pSetSleepModeParams); } NvAPI_Status __cdecl NvAPI_Vulkan_Sleep(__in HANDLE vkDevice, __in NvU64 signalValue) { - return OK(); + if (!Init()) + return ERROR(); + + if (!vkDevice) + return ERROR(); + + return lowlatency_ctx.Sleep(vkDevice); } NvAPI_Status __cdecl NvAPI_Vulkan_GetLatency(__in HANDLE vkDevice, __inout NV_VULKAN_LATENCY_RESULT_PARAMS* pGetLatencyParams) { - return OK(); + if (!Init()) + return ERROR(); + + if (pGetLatencyParams == nullptr) + return ERROR_VALUE(NVAPI_INVALID_ARGUMENT); + + return lowlatency_ctx.GetLatency(vkDevice, pGetLatencyParams); } NvAPI_Status __cdecl NvAPI_Vulkan_SetLatencyMarker(__in HANDLE vkDevice, __in NV_VULKAN_LATENCY_MARKER_PARAMS* pSetLatencyMarkerParams) { - return OK(); + if (!Init()) + return ERROR(); + + if (!vkDevice) + return ERROR(); + + return lowlatency_ctx.SetLatencyMarker(vkDevice, pSetLatencyMarkerParams); } NvAPI_Status __cdecl NvAPI_Vulkan_NotifyOutOfBandVkQueue(__in HANDLE vkDevice, __in HANDLE queueHandle, __in NV_VULKAN_OUT_OF_BAND_QUEUE_TYPE queueType) { diff --git a/OptiScaler/nvapi/fakenvapi/low_latency.cpp b/OptiScaler/nvapi/fakenvapi/low_latency.cpp index 8e5c785d..b8535b6a 100644 --- a/OptiScaler/nvapi/fakenvapi/low_latency.cpp +++ b/OptiScaler/nvapi/fakenvapi/low_latency.cpp @@ -63,6 +63,32 @@ bool LowLatency::update_low_latency_tech(IUnknown* pDevice) { return true; } +bool LowLatency::update_low_latency_tech(HANDLE vkDevice) { + if (!currently_active_tech) { + currently_active_tech = new LatencyFlex(); + if (currently_active_tech->init(nullptr)) { + spdlog::debug("LowLatency algo: LatencyFlex"); + return true; + } + } + + static bool last_force_latencyflex = Config::get().get_force_latencyflex(); + bool force_latencyflex = Config::get().get_force_latencyflex(); + bool change_detected = last_force_latencyflex != force_latencyflex; + last_force_latencyflex = force_latencyflex; + + if (change_detected) { + if (deinit_current_tech()) { + return update_low_latency_tech((HANDLE) nullptr); // call back to reinit + } else { + spdlog::error("Couldn't deinitialize low latency tech"); + return false; + } + } + + return true; +} + void LowLatency::update_effective_fg_state() { if (!currently_active_tech) return; @@ -123,12 +149,62 @@ void LowLatency::add_marker_to_report(NV_LATENCY_MARKER_PARAMS* pSetLatencyMarke } } +void LowLatency::add_marker_to_report(NV_VULKAN_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 = (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 (pSetLatencyMarkerParams->markerType) { + case VULKAN_SIMULATION_START: + _2nd_last_sim_start = last_sim_start; + last_sim_start = get_timestamp() / 1000; + current_report->simStartTime = last_sim_start; + break; + case VULKAN_SIMULATION_END: + current_report->simEndTime = get_timestamp() / 1000; + break; + case VULKAN_RENDERSUBMIT_START: + current_report->renderSubmitStartTime = get_timestamp() / 1000; + break; + case VULKAN_RENDERSUBMIT_END: + current_report->renderSubmitEndTime = get_timestamp() / 1000; + break; + case VULKAN_PRESENT_START: + current_report->presentStartTime = get_timestamp() / 1000; + break; + case VULKAN_PRESENT_END: + current_report->presentEndTime = get_timestamp() / 1000; + break; + case VULKAN_INPUT_SAMPLE: + current_report->inputSampleTime = get_timestamp() / 1000; + break; + default: + break; + } +} + void LowLatency::get_latency_result(NV_LATENCY_RESULT_PARAMS* pGetLatencyParams) { for (auto i = 0; i < 64; i++) { memcpy(&pGetLatencyParams->frameReport[i], &frame_reports[i], sizeof(frame_reports[i])); } } +void LowLatency::get_latency_result(NV_VULKAN_LATENCY_RESULT_PARAMS* pGetLatencyParams) { + for (auto i = 0; i < 64; i++) { + // some data is sent into rsvd + memcpy(&pGetLatencyParams->frameReport[i], &frame_reports[i], sizeof(frame_reports[i])); + } +} + void LowLatency::get_low_latency_context(void** low_latency_context, Mode* low_latency_tech) { if (!currently_active_tech || !low_latency_context) return; @@ -137,6 +213,52 @@ void LowLatency::get_low_latency_context(void** low_latency_context, Mode* low_l *low_latency_tech = currently_active_tech->get_mode(); } +std::string LowLatency::marker_to_name(uint32_t marker) { + switch (marker) { + case SIMULATION_START: + return "SIMULATION_START"; + break; + case SIMULATION_END: + return "SIMULATION_END"; + break; + case RENDERSUBMIT_START: + return "RENDERSUBMIT_START"; + break; + case RENDERSUBMIT_END: + return "RENDERSUBMIT_END"; + break; + case PRESENT_START: + return "PRESENT_START"; + break; + case PRESENT_END: + return "PRESENT_END"; + break; + case INPUT_SAMPLE: + return "INPUT_SAMPLE"; + break; + case TRIGGER_FLASH: + return "TRIGGER_FLASH"; + break; + case PC_LATENCY_PING: + return "PC_LATENCY_PING"; + break; + case OUT_OF_BAND_RENDERSUBMIT_START: + return "OUT_OF_BAND_RENDERSUBMIT_START"; + break; + case OUT_OF_BAND_RENDERSUBMIT_END: + return "OUT_OF_BAND_RENDERSUBMIT_END"; + break; + case OUT_OF_BAND_PRESENT_START: + return "OUT_OF_BAND_PRESENT_START"; + break; + case OUT_OF_BAND_PRESENT_END: + return "OUT_OF_BAND_PRESENT_END"; + break; + } + + return "Unknown"; +} + NvAPI_Status LowLatency::GetSleepStatus(IUnknown *pDevice, NV_GET_SLEEP_STATUS_PARAMS *pGetSleepStatusParams) { if (!update_low_latency_tech(pDevice)) @@ -153,6 +275,20 @@ NvAPI_Status LowLatency::GetSleepStatus(IUnknown *pDevice, NV_GET_SLEEP_STATUS_P return OK(); } +NvAPI_Status LowLatency::GetSleepStatus(HANDLE vkDevice, NV_VULKAN_GET_SLEEP_STATUS_PARAMS* pGetSleepStatusParams) +{ + if (!update_low_latency_tech(vkDevice)) + return ERROR(); + + SleepParams sleep_params{}; + + currently_active_tech->get_sleep_status(&sleep_params); + + pGetSleepStatusParams->bLowLatencyMode = sleep_params.low_latency_enabled; + + return OK(); +} + NvAPI_Status LowLatency::SetSleepMode(IUnknown* pDevice, NV_SET_SLEEP_MODE_PARAMS* pSetSleepModeParams) { if (!update_low_latency_tech(pDevice)) return ERROR(); @@ -169,6 +305,22 @@ NvAPI_Status LowLatency::SetSleepMode(IUnknown* pDevice, NV_SET_SLEEP_MODE_PARAM return OK(); } +NvAPI_Status LowLatency::SetSleepMode(HANDLE vkDevice, NV_VULKAN_SET_SLEEP_MODE_PARAMS* pSetSleepModeParams) { + if (!update_low_latency_tech(vkDevice)) + return ERROR(); + + SleepMode sleep_mode{}; + + sleep_mode.low_latency_enabled = pSetSleepModeParams->bLowLatencyMode; + sleep_mode.low_latency_boost = pSetSleepModeParams->bLowLatencyBoost; + sleep_mode.minimum_interval_us = pSetSleepModeParams->minimumIntervalUs; + sleep_mode.use_markers_to_optimize = true; + + currently_active_tech->set_sleep_mode(&sleep_mode); + + return OK(); +} + NvAPI_Status LowLatency::Sleep(IUnknown* pDevice) { if (!update_low_latency_tech(pDevice)) return ERROR(); @@ -178,6 +330,15 @@ NvAPI_Status LowLatency::Sleep(IUnknown* pDevice) { return OK(); } +NvAPI_Status LowLatency::Sleep(HANDLE vkDevice) { + if (!update_low_latency_tech(vkDevice)) + return ERROR(); + + currently_active_tech->sleep(); + + return OK(); +} + NvAPI_Status LowLatency::SetLatencyMarker(IUnknown* pDev, NV_LATENCY_MARKER_PARAMS* pSetLatencyMarkerParams) { if (!update_low_latency_tech(pDev)) return ERROR(); @@ -195,7 +356,31 @@ NvAPI_Status LowLatency::SetLatencyMarker(IUnknown* pDev, NV_LATENCY_MARKER_PARA currently_active_tech->set_marker(&marker_params); - return OK(); + spdlog::trace("{}: {}", marker_to_name(pSetLatencyMarkerParams->markerType), pSetLatencyMarkerParams->frameID); + + return NVAPI_OK; +} + +NvAPI_Status LowLatency::SetLatencyMarker(HANDLE vkDevice, NV_VULKAN_LATENCY_MARKER_PARAMS* pSetLatencyMarkerParams) { + if (!update_low_latency_tech(vkDevice)) + return ERROR(); + + update_effective_fg_state(); + + update_enabled_override(); + + add_marker_to_report(pSetLatencyMarkerParams); + + MarkerParams marker_params{}; + + marker_params.frame_id = pSetLatencyMarkerParams->frameID; + marker_params.marker_type = (MarkerType) pSetLatencyMarkerParams->markerType; // requires enums to match + + currently_active_tech->set_marker(&marker_params); + + spdlog::trace("{}: {}", marker_to_name(pSetLatencyMarkerParams->markerType), pSetLatencyMarkerParams->frameID); + + return NVAPI_OK; } NvAPI_Status LowLatency::SetAsyncFrameMarker(ID3D12CommandQueue* pCommandQueue, NV_ASYNC_FRAME_MARKER_PARAMS* pSetAsyncFrameMarkerParams) { @@ -233,4 +418,22 @@ NvAPI_Status LowLatency::SetAsyncFrameMarker(ID3D12CommandQueue* pCommandQueue, currently_active_tech->set_async_marker(&marker_params); return OK(); -} \ No newline at end of file +} + +NvAPI_Status LowLatency::GetLatency(IUnknown* pDev, NV_LATENCY_RESULT_PARAMS* pGetLatencyParams) { + if (!update_low_latency_tech(pDev)) + return ERROR(); + + get_latency_result(pGetLatencyParams); + + return OK(); +} + +NvAPI_Status LowLatency::GetLatency(HANDLE vkDevice, NV_VULKAN_LATENCY_RESULT_PARAMS* pGetLatencyParams) { + if (!update_low_latency_tech(vkDevice)) + return ERROR(); + + get_latency_result(pGetLatencyParams); + + return OK(); +} diff --git a/OptiScaler/nvapi/fakenvapi/low_latency.h b/OptiScaler/nvapi/fakenvapi/low_latency.h index 50177827..bf91d38b 100644 --- a/OptiScaler/nvapi/fakenvapi/low_latency.h +++ b/OptiScaler/nvapi/fakenvapi/low_latency.h @@ -42,7 +42,10 @@ private: bool update_low_latency_tech(IUnknown* pDevice); void update_effective_fg_state(); void update_enabled_override(); + void get_latency_result(NV_LATENCY_RESULT_PARAMS* pGetLatencyParams); void add_marker_to_report(NV_LATENCY_MARKER_PARAMS *pSetLatencyMarkerParams); + void add_marker_to_report(NV_VULKAN_LATENCY_MARKER_PARAMS *pSetLatencyMarkerParams); + inline std::string marker_to_name(uint32_t marker); void update_config(); public: @@ -50,7 +53,6 @@ public: ~LowLatency() { deinit_current_tech(); }; bool deinit_current_tech(); - void get_latency_result(NV_LATENCY_RESULT_PARAMS* pGetLatencyParams); void set_forced_fg(std::optional forced_fg) { this->forced_fg = forced_fg; }; void set_fg_type(bool interpolated, uint64_t frame_id) { currently_active_tech->set_fg_type(interpolated, frame_id); } void get_low_latency_context(void** low_latency_context, Mode* low_latency_tech); @@ -58,7 +60,16 @@ public: NvAPI_Status GetSleepStatus(IUnknown* pDevice, NV_GET_SLEEP_STATUS_PARAMS* pGetSleepStatusParams); NvAPI_Status SetSleepMode(IUnknown* pDevice, NV_SET_SLEEP_MODE_PARAMS* pSetSleepModeParams); NvAPI_Status Sleep(IUnknown* pDevice); - NvAPI_Status SetLatencyMarker(IUnknown* pDev, NV_LATENCY_MARKER_PARAMS* pSetLatencyMarkerParams); - NvAPI_Status SetAsyncFrameMarker(ID3D12CommandQueue* pCommandQueue, NV_ASYNC_FRAME_MARKER_PARAMS* pSetAsyncFrameMarkerParams); + NvAPI_Status SetLatencyMarker(IUnknown *pDev, NV_LATENCY_MARKER_PARAMS *pSetLatencyMarkerParams); + NvAPI_Status SetAsyncFrameMarker(ID3D12CommandQueue *pCommandQueue, NV_ASYNC_FRAME_MARKER_PARAMS *pSetAsyncFrameMarkerParams); NvAPI_Status GetLatency(IUnknown* pDev, NV_LATENCY_RESULT_PARAMS* pGetLatencyParams); + + // Vulkan + bool update_low_latency_tech(HANDLE vkDevice); + void get_latency_result(NV_VULKAN_LATENCY_RESULT_PARAMS *pGetLatencyParams); + NvAPI_Status SetLatencyMarker(HANDLE vkDevice, NV_VULKAN_LATENCY_MARKER_PARAMS *pSetLatencyMarkerParams); + NvAPI_Status Sleep(HANDLE vkDevice); + NvAPI_Status SetSleepMode(HANDLE vkDevice, NV_VULKAN_SET_SLEEP_MODE_PARAMS* pSetSleepModeParams); + NvAPI_Status GetSleepStatus(HANDLE vkDevice, NV_VULKAN_GET_SLEEP_STATUS_PARAMS* pGetSleepStatusParams); + NvAPI_Status GetLatency(HANDLE vkDevice, NV_VULKAN_LATENCY_RESULT_PARAMS* pGetLatencyParams); }; \ No newline at end of file