diff --git a/OptiScaler/nvapi/fakenvapi/ll_antilag2.cpp b/OptiScaler/nvapi/fakenvapi/ll_antilag2.cpp index 0da79151..e067bc16 100644 --- a/OptiScaler/nvapi/fakenvapi/ll_antilag2.cpp +++ b/OptiScaler/nvapi/fakenvapi/ll_antilag2.cpp @@ -106,7 +106,7 @@ void AntiLag2::sleep() { al2_sleep(); } -void AntiLag2::set_marker(MarkerParams* marker_params) { +void AntiLag2::set_marker(IUnknown* pDevice, MarkerParams* marker_params) { switch(marker_params->marker_type) { case MarkerType::SIMULATION_START: if (current_call_spot == CallSpot::SimulationStart) diff --git a/OptiScaler/nvapi/fakenvapi/ll_antilag2.h b/OptiScaler/nvapi/fakenvapi/ll_antilag2.h index a07635df..8fce2fef 100644 --- a/OptiScaler/nvapi/fakenvapi/ll_antilag2.h +++ b/OptiScaler/nvapi/fakenvapi/ll_antilag2.h @@ -32,6 +32,6 @@ public: void get_sleep_status(SleepParams* sleep_params) override; void set_sleep_mode(SleepMode* sleep_mode) override; void sleep() override; - void set_marker(MarkerParams* marker_params) override; + void set_marker(IUnknown* pDevice, MarkerParams* marker_params) override; void set_async_marker(MarkerParams* marker_params) override; }; \ No newline at end of file diff --git a/OptiScaler/nvapi/fakenvapi/ll_antilag_vk.cpp b/OptiScaler/nvapi/fakenvapi/ll_antilag_vk.cpp new file mode 100644 index 00000000..70a17bc0 --- /dev/null +++ b/OptiScaler/nvapi/fakenvapi/ll_antilag_vk.cpp @@ -0,0 +1,87 @@ +#include "ll_antilag_vk.h" +#include "vulkan_hooks.h" + +bool AntiLagVk::init(IUnknown* pDevice) { + return VulkanHooks::o_vkAntiLagUpdateAMD != nullptr; +} + +void* AntiLagVk::get_tech_context() { + return nullptr; +} + +void AntiLagVk::get_sleep_status(SleepParams* sleep_params) { + sleep_params->low_latency_enabled = is_enabled(); + sleep_params->fullscreen_vrr = true; + sleep_params->control_panel_vsync_override = false; +} + +void AntiLagVk::set_sleep_mode(SleepMode* sleep_mode) { + // UNUSED: + // low_latency_boost + // use_markers_to_optimize + + low_latency_enabled = sleep_mode->low_latency_enabled; + minimum_interval_us = sleep_mode->minimum_interval_us; // don't convert to fps due to fg fps limit fallback using intervals +} + +void AntiLagVk::sleep() { + // This can be supported but prefer using markers with frame ids instead + return; +} + +void AntiLagVk::set_marker(IUnknown* pDevice, MarkerParams* marker_params) { + auto mode = is_enabled() ? VK_ANTI_LAG_MODE_ON_AMD : VK_ANTI_LAG_MODE_OFF_AMD; + auto max_fps = minimum_interval_us > 0 ? (uint32_t) std::round(1000000.0f / minimum_interval_us) : 0; + + if (marker_params->marker_type == MarkerType::SIMULATION_START) + { + // Before processing user input + VkAntiLagPresentationInfoAMD inputInfo = {}; + inputInfo.sType = VK_STRUCTURE_TYPE_ANTI_LAG_PRESENTATION_INFO_AMD; + inputInfo.stage = VK_ANTI_LAG_STAGE_INPUT_AMD; + inputInfo.frameIndex = marker_params->frame_id; + + VkAntiLagDataAMD antiLagDataInput = {}; + antiLagDataInput.sType = VK_STRUCTURE_TYPE_ANTI_LAG_DATA_AMD; + antiLagDataInput.mode = mode; + antiLagDataInput.pPresentationInfo = &inputInfo; + antiLagDataInput.maxFPS = max_fps; // Or your desired FPS cap + + if (VulkanHooks::o_vkAntiLagUpdateAMD) + { + spdlog::trace("AntiLag Input: {}, status: {}", marker_params->frame_id, is_enabled()); + + VulkanHooks::o_vkAntiLagUpdateAMD((VkDevice)pDevice, &antiLagDataInput); + } + } + + if (marker_params->marker_type == MarkerType::PRESENT_START) + { + // Before calling vkQueuePresentKHR + VkAntiLagPresentationInfoAMD presentInfo = {}; + presentInfo.sType = VK_STRUCTURE_TYPE_ANTI_LAG_PRESENTATION_INFO_AMD; + presentInfo.stage = VK_ANTI_LAG_STAGE_PRESENT_AMD; + presentInfo.frameIndex = marker_params->frame_id; + + VkAntiLagDataAMD antiLagDataPresent = {}; + antiLagDataPresent.sType = VK_STRUCTURE_TYPE_ANTI_LAG_DATA_AMD; + antiLagDataPresent.mode = mode; + antiLagDataPresent.pPresentationInfo = &presentInfo; + antiLagDataPresent.maxFPS = max_fps; // Or your desired FPS cap + + if (VulkanHooks::o_vkAntiLagUpdateAMD) + { + spdlog::trace("AntiLag Present: {}, status: {}", marker_params->frame_id, is_enabled()); + + VulkanHooks::o_vkAntiLagUpdateAMD((VkDevice)pDevice, &antiLagDataPresent); + } + } +} + +void AntiLagVk::set_async_marker(MarkerParams* marker_params) { + if (marker_params->marker_type == MarkerType::OUT_OF_BAND_PRESENT_START) { + static uint64_t previous_frame_id = marker_params->frame_id; + set_fg_type(previous_frame_id == marker_params->frame_id, marker_params->frame_id); + previous_frame_id = marker_params->frame_id; + } +} diff --git a/OptiScaler/nvapi/fakenvapi/ll_antilag_vk.h b/OptiScaler/nvapi/fakenvapi/ll_antilag_vk.h new file mode 100644 index 00000000..87c6c236 --- /dev/null +++ b/OptiScaler/nvapi/fakenvapi/ll_antilag_vk.h @@ -0,0 +1,29 @@ +#pragma once + +#include "low_latency_tech.h" + +class AntiLagVk : public virtual LowLatencyTech { +private: + uint32_t minimum_interval_us = 0; + +public: + AntiLagVk(): LowLatencyTech() {} + + // From LowLatencyTech + bool init(IUnknown *pDevice) override; + void deinit() override {}; // Not used by AntiLag VK + + Mode get_mode() override { return Mode::AntiLagVk; }; + void* get_tech_context() override; + void set_fg_type(bool interpolated, uint64_t frame_id) override {}; // Not used by AntiLag VK + void set_low_latency_override(ForceReflex low_latency_override) override { this->low_latency_override = low_latency_override; }; + void set_effective_fg_state(bool effective_fg_state) override { this->effective_fg_state = effective_fg_state; }; + + bool is_enabled() override { return low_latency_override != ForceReflex::InGame ? low_latency_override == ForceReflex::ForceEnable : low_latency_enabled; }; + + void get_sleep_status(SleepParams* sleep_params) override; + void set_sleep_mode(SleepMode* sleep_mode) override; + void sleep() override; + void set_marker(IUnknown* pDevice, MarkerParams* marker_params) override; + void set_async_marker(MarkerParams* marker_params) override; +}; \ No newline at end of file diff --git a/OptiScaler/nvapi/fakenvapi/ll_latencyflex.cpp b/OptiScaler/nvapi/fakenvapi/ll_latencyflex.cpp index 294bcf7c..deb428fa 100644 --- a/OptiScaler/nvapi/fakenvapi/ll_latencyflex.cpp +++ b/OptiScaler/nvapi/fakenvapi/ll_latencyflex.cpp @@ -117,7 +117,7 @@ void LatencyFlex::sleep() { lfx_sleep(INVALID_ID); }; -void LatencyFlex::set_marker(MarkerParams* marker_params) { +void LatencyFlex::set_marker(IUnknown* pDevice, MarkerParams* marker_params) { switch(marker_params->marker_type) { case MarkerType::SIMULATION_START: if (current_call_spot == CallSpot::SimulationStart) diff --git a/OptiScaler/nvapi/fakenvapi/ll_latencyflex.h b/OptiScaler/nvapi/fakenvapi/ll_latencyflex.h index 7e2aec82..d4327212 100644 --- a/OptiScaler/nvapi/fakenvapi/ll_latencyflex.h +++ b/OptiScaler/nvapi/fakenvapi/ll_latencyflex.h @@ -39,6 +39,6 @@ public: void get_sleep_status(SleepParams* sleep_params) override; void set_sleep_mode(SleepMode* sleep_mode) override; void sleep() override; - void set_marker(MarkerParams* marker_params) override; + void set_marker(IUnknown* pDevice, MarkerParams* marker_params) override; void set_async_marker(MarkerParams* marker_params) override {}; // Not used by LFX }; \ No newline at end of file diff --git a/OptiScaler/nvapi/fakenvapi/ll_xell.cpp b/OptiScaler/nvapi/fakenvapi/ll_xell.cpp index 23f41711..acd00ee7 100644 --- a/OptiScaler/nvapi/fakenvapi/ll_xell.cpp +++ b/OptiScaler/nvapi/fakenvapi/ll_xell.cpp @@ -133,7 +133,7 @@ void XeLL::set_sleep_mode(SleepMode* sleep_mode) { auto result = o_xellSetSleepMode(ctx, &xell_sleep_params); } -void XeLL::set_marker(MarkerParams* marker_params) { +void XeLL::set_marker(IUnknown* pDevice, MarkerParams* marker_params) { switch (marker_params->marker_type) { case MarkerType::SIMULATION_START: xell_sleep(marker_params->frame_id); diff --git a/OptiScaler/nvapi/fakenvapi/ll_xell.h b/OptiScaler/nvapi/fakenvapi/ll_xell.h index 9725afbb..cbb6a5d5 100644 --- a/OptiScaler/nvapi/fakenvapi/ll_xell.h +++ b/OptiScaler/nvapi/fakenvapi/ll_xell.h @@ -43,6 +43,6 @@ public: void get_sleep_status(SleepParams* sleep_params) override; void set_sleep_mode(SleepMode* sleep_mode) override; void sleep() override {}; // Not used by XeLL due to missing frame ids - void set_marker(MarkerParams* marker_params) override; + void set_marker(IUnknown* pDevice, MarkerParams* marker_params) override; void set_async_marker(MarkerParams* marker_params) override {}; // Not used by XeLL }; \ No newline at end of file diff --git a/OptiScaler/nvapi/fakenvapi/low_latency.cpp b/OptiScaler/nvapi/fakenvapi/low_latency.cpp index 5809b9f7..75e5b95f 100644 --- a/OptiScaler/nvapi/fakenvapi/low_latency.cpp +++ b/OptiScaler/nvapi/fakenvapi/low_latency.cpp @@ -1,5 +1,6 @@ #include "low_latency.h" #include "ll_antilag2.h" +#include "ll_antilag_vk.h" #include "ll_latencyflex.h" #include "ll_xell.h" @@ -65,9 +66,19 @@ bool LowLatency::update_low_latency_tech(IUnknown* pDevice) { bool LowLatency::update_low_latency_tech(HANDLE vkDevice) { if (!currently_active_tech) { + if (!Config::get().get_force_latencyflex()) { + currently_active_tech = new AntiLagVk(); + if (currently_active_tech->init(nullptr)) { + spdlog::info("LowLatency algo: AntiLag Vulkan"); + return true; + } + + delete currently_active_tech; + } + currently_active_tech = new LatencyFlex(); if (currently_active_tech->init(nullptr)) { - spdlog::debug("LowLatency algo: LatencyFlex"); + spdlog::info("LowLatency algo: LatencyFlex"); return true; } } @@ -354,7 +365,7 @@ NvAPI_Status LowLatency::SetLatencyMarker(IUnknown* pDev, NV_LATENCY_MARKER_PARA marker_params.frame_id = pSetLatencyMarkerParams->frameID; marker_params.marker_type = (MarkerType) pSetLatencyMarkerParams->markerType; // requires enums to match - currently_active_tech->set_marker(&marker_params); + currently_active_tech->set_marker(pDev, &marker_params); spdlog::trace("{}: {}", marker_to_name(pSetLatencyMarkerParams->markerType), pSetLatencyMarkerParams->frameID); @@ -376,7 +387,8 @@ NvAPI_Status LowLatency::SetLatencyMarker(HANDLE vkDevice, NV_VULKAN_LATENCY_MAR marker_params.frame_id = pSetLatencyMarkerParams->frameID; marker_params.marker_type = (MarkerType) pSetLatencyMarkerParams->markerType; // requires enums to match - currently_active_tech->set_marker(&marker_params); + // This cast is not ideal as it needs to be cast to VkDevice while knowing it's vulkan + currently_active_tech->set_marker((IUnknown*)vkDevice, &marker_params); spdlog::trace("{}: {}", marker_to_name(pSetLatencyMarkerParams->markerType), pSetLatencyMarkerParams->frameID); diff --git a/OptiScaler/nvapi/fakenvapi/low_latency_tech.h b/OptiScaler/nvapi/fakenvapi/low_latency_tech.h index 5ee35b86..2a641675 100644 --- a/OptiScaler/nvapi/fakenvapi/low_latency_tech.h +++ b/OptiScaler/nvapi/fakenvapi/low_latency_tech.h @@ -14,7 +14,8 @@ enum class Mode { AntiLag2, LatencyFlex, - XeLL + XeLL, + AntiLagVk }; enum class CallSpot { @@ -87,6 +88,6 @@ public: virtual void get_sleep_status(SleepParams* sleep_params) = 0; virtual void set_sleep_mode(SleepMode* sleep_mode) = 0; virtual void sleep() = 0; - virtual void set_marker(MarkerParams* marker_params) = 0; + virtual void set_marker(IUnknown* pDevice, MarkerParams* marker_params) = 0; virtual void set_async_marker(MarkerParams* marker_params) = 0; }; \ No newline at end of file diff --git a/OptiScaler/nvapi/fakenvapi/main.cpp b/OptiScaler/nvapi/fakenvapi/main.cpp index b04c827b..1ab5f54f 100755 --- a/OptiScaler/nvapi/fakenvapi/main.cpp +++ b/OptiScaler/nvapi/fakenvapi/main.cpp @@ -11,6 +11,7 @@ #endif #include "../external/nvapi/nvapi.h" #include "fakenvapi.h" +#include "vulkan_hooks.h" #include "../version.h" #include "log.h" @@ -37,6 +38,12 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { spdlog::info("Config lfx_mode: {}", (int)Config::get().get_latencyflex_mode()); spdlog::info("Config save_pcl_to_file: {}", Config::get().get_save_pcl_to_file() ? "true" : "false"); + { + // TODO: won't work for late loaded vulkan-1.dll + auto vulkan_module = GetModuleHandleA("vulkan-1.dll"); + VulkanHooks::hook_vulkan(vulkan_module); + } + break; case DLL_PROCESS_DETACH: LowLatencyCtx::get()->deinit_current_tech(); diff --git a/OptiScaler/nvapi/fakenvapi/meson.build b/OptiScaler/nvapi/fakenvapi/meson.build index 23054b1b..b196876c 100644 --- a/OptiScaler/nvapi/fakenvapi/meson.build +++ b/OptiScaler/nvapi/fakenvapi/meson.build @@ -1,9 +1,9 @@ dll = shared_library( 'nvapi'+target_suffix, - ['main.cpp', 'fakenvapi.cpp', 'low_latency.cpp', 'low_latency_tech.cpp', 'll_antilag2.cpp', 'll_xell.cpp', 'll_latencyflex.cpp', 'util.cpp', 'log.cpp', fakenvapi_version], + ['main.cpp', 'fakenvapi.cpp', 'low_latency.cpp', 'low_latency_tech.cpp', 'll_antilag2.cpp', 'll_antilag_vk.cpp', 'll_xell.cpp', 'll_latencyflex.cpp', 'vulkan_hooks.cpp', 'util.cpp', 'log.cpp', fakenvapi_version], rc, name_prefix : '', - dependencies : [ lib_dxgi ], - include_directories: [ spdlog_headers, xell_headers ], + dependencies : [ lib_dxgi, lib_detours ], + include_directories: [ spdlog_headers, xell_headers, detours_headers, vulkan_headers ], install: true ) diff --git a/OptiScaler/nvapi/fakenvapi/vulkan_hooks.cpp b/OptiScaler/nvapi/fakenvapi/vulkan_hooks.cpp new file mode 100644 index 00000000..b3166f94 --- /dev/null +++ b/OptiScaler/nvapi/fakenvapi/vulkan_hooks.cpp @@ -0,0 +1,67 @@ +#include "vulkan_hooks.h" + +#include +#include +#include + +PFN_vkCreateDevice VulkanHooks::o_vkCreateDevice = nullptr; +PFN_vkGetPhysicalDeviceFeatures2 VulkanHooks::o_vkGetPhysicalDeviceFeatures2 = nullptr; +PFN_vkGetDeviceProcAddr VulkanHooks::o_vkGetDeviceProcAddr = nullptr; +PFN_vkAntiLagUpdateAMD VulkanHooks::o_vkAntiLagUpdateAMD = nullptr; + +VkResult VulkanHooks::hkvkCreateDevice(VkPhysicalDevice physicalDevice, VkDeviceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDevice* pDevice) { + spdlog::debug("hkvkCreateDevice"); + + std::vector extensions(pCreateInfo->ppEnabledExtensionNames, pCreateInfo->ppEnabledExtensionNames + pCreateInfo->enabledExtensionCount); + + // AntiLag + bool antiLagSupported = false; + VkPhysicalDeviceFeatures2 features2 = {}; + features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + + VkPhysicalDeviceAntiLagFeaturesAMD antiLagFeatures = {}; + antiLagFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ANTI_LAG_FEATURES_AMD; + + features2.pNext = &antiLagFeatures; + + if (o_vkGetPhysicalDeviceFeatures2) { + o_vkGetPhysicalDeviceFeatures2(physicalDevice, &features2); + + if (antiLagFeatures.antiLag) { + antiLagSupported = true; + spdlog::info("Adding AntiLag extension"); + extensions.push_back(VK_AMD_ANTI_LAG_EXTENSION_NAME); + } + } + + // TODO add check for VK_KHR_DEVICE_GROUP_CREATION_EXTENSION_NAME as AntiLag doesn't support it + + pCreateInfo->enabledExtensionCount = static_cast(extensions.size()); + pCreateInfo->ppEnabledExtensionNames = extensions.data(); + + auto result = o_vkCreateDevice(physicalDevice, pCreateInfo, pAllocator, pDevice); + + // Can ask for a function from an extension after the device creation + if (antiLagSupported && o_vkGetDeviceProcAddr) { + o_vkAntiLagUpdateAMD = (PFN_vkAntiLagUpdateAMD) o_vkGetDeviceProcAddr(*pDevice, "vkAntiLagUpdateAMD"); + } else { + spdlog::info("Vulkan AntiLag can't be enabled"); + } + + return result; +} +void VulkanHooks::hook_vulkan(HMODULE vulkanModule) { + spdlog::debug("Trying to hook Vulkan"); + + o_vkCreateDevice = (PFN_vkCreateDevice) GetProcAddress(vulkanModule, "vkCreateDevice"); + o_vkGetPhysicalDeviceFeatures2 = (PFN_vkGetPhysicalDeviceFeatures2) GetProcAddress(vulkanModule, "vkGetPhysicalDeviceFeatures2"); + o_vkGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr) GetProcAddress(vulkanModule, "vkGetDeviceProcAddr"); + + DetourTransactionBegin(); + DetourUpdateThread(GetCurrentThread()); + + if (o_vkCreateDevice) + DetourAttach(&(PVOID&) o_vkCreateDevice, hkvkCreateDevice); + + DetourTransactionCommit(); +} \ No newline at end of file diff --git a/OptiScaler/nvapi/fakenvapi/vulkan_hooks.h b/OptiScaler/nvapi/fakenvapi/vulkan_hooks.h new file mode 100644 index 00000000..b63d2381 --- /dev/null +++ b/OptiScaler/nvapi/fakenvapi/vulkan_hooks.h @@ -0,0 +1,19 @@ +#pragma once + +#include "log.h" + +#include +#include +#include + +class VulkanHooks { +private: + static PFN_vkCreateDevice o_vkCreateDevice; + static PFN_vkGetPhysicalDeviceFeatures2 o_vkGetPhysicalDeviceFeatures2; + static PFN_vkGetDeviceProcAddr o_vkGetDeviceProcAddr; + +public: + static PFN_vkAntiLagUpdateAMD o_vkAntiLagUpdateAMD; + static void hook_vulkan(HMODULE vulkanModule); + static VkResult hkvkCreateDevice(VkPhysicalDevice physicalDevice, VkDeviceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkDevice *pDevice); +};