From b9b57a517cf37c195ee59fd70c93b97451ec9d7a Mon Sep 17 00:00:00 2001 From: Tatsuyuki Ishi Date: Fri, 24 Dec 2021 13:37:43 +0900 Subject: [PATCH 1/3] nvapi: Reflex support through LatencyFleX --- README.md | 1 + src/d3d/lfx.cpp | 42 ++++++++++++ src/d3d/lfx.h | 26 ++++++++ src/d3d/nvapi_d3d_instance.cpp | 39 +++++++++++ src/d3d/nvapi_d3d_instance.h | 23 +++++++ src/meson.build | 4 +- src/nvapi.cpp | 3 + src/nvapi_d3d.cpp | 56 ++++++++++++++++ src/nvapi_interface.cpp | 3 + src/nvapi_static.h | 4 +- src/{sysinfo => }/resource_factory.cpp | 4 ++ src/{sysinfo => }/resource_factory.h | 10 +-- src/sysinfo/nvapi_adapter_registry.h | 2 +- tests/meson.build | 4 +- tests/mock_factory.cpp | 15 +++-- tests/nvapi_d3d.cpp | 58 +++++++++++++++++ tests/nvapi_d3d12.cpp | 7 +- tests/nvapi_d3d_mocks.cpp | 6 ++ tests/nvapi_sysinfo.cpp | 65 ++++++++++--------- tests/nvapi_tests.cpp | 2 +- ...nfo_util.cpp => resource_factory_util.cpp} | 15 +++-- 21 files changed, 338 insertions(+), 51 deletions(-) create mode 100644 src/d3d/lfx.cpp create mode 100644 src/d3d/lfx.h create mode 100644 src/d3d/nvapi_d3d_instance.cpp create mode 100644 src/d3d/nvapi_d3d_instance.h rename src/{sysinfo => }/resource_factory.cpp (85%) rename src/{sysinfo => }/resource_factory.h (63%) rename tests/{nvapi_sysinfo_util.cpp => resource_factory_util.cpp} (95%) diff --git a/README.md b/README.md index 337f3ae8..42bfaa15 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ This implementation currently offers entry points for supporting the following f - NVIDIA DLSS for Vulkan, by supporting the relevant adapter information by querying from [Vulkan](https://www.vulkan.org/). - NVIDIA DLSS for D3D11 and D3D12, by querying from Vulkan and forwarding the relevant calls into DXVK / VKD3D-Proton. +- NVIDIA Reflex Low-Latency bridged to [LatencyFleX](https://github.com/ishitatsuyuki/LatencyFleX). This is not the official implementation and this requires additional setup. - Several NVAPI D3D11 extensions, among others `SetDepthBoundsTest` and `UAVOverlap`, by forwarding the relevant calls into DXVK. - NVIDIA PhysX, by supporting entry points for querying PhysX capability. - Several GPU topology related methods for adapter and display information, by querying from DXVK and Vulkan. diff --git a/src/d3d/lfx.cpp b/src/d3d/lfx.cpp new file mode 100644 index 00000000..fced8629 --- /dev/null +++ b/src/d3d/lfx.cpp @@ -0,0 +1,42 @@ +#include "lfx.h" + +#include +#include "../util/util_string.h" +#include "../util/util_log.h" + +namespace dxvk { + Lfx::Lfx() { + const auto lfxModuleName = "latencyflex_wine.dll"; + m_lfxModule = ::LoadLibraryA(lfxModuleName); + if (m_lfxModule == nullptr) { + auto lastError = ::GetLastError(); + if (lastError != ERROR_MOD_NOT_FOUND) // Ignore library not found + log::write(str::format("Loading ", lfxModuleName, " failed with error code: ", lastError)); + + return; + } + + m_winelfx_WaitAndBeginFrame = reinterpret_cast(reinterpret_cast(GetProcAddress(m_lfxModule, "winelfx_WaitAndBeginFrame"))); + m_winelfx_SetTargetFrameTime = reinterpret_cast(reinterpret_cast(GetProcAddress(m_lfxModule, "winelfx_SetTargetFrameTime"))); + } + + Lfx::~Lfx() { + if (m_lfxModule == nullptr) return; + ::FreeLibrary(m_lfxModule); + m_lfxModule = nullptr; + } + + bool Lfx::IsAvailable() const { + return m_lfxModule != nullptr; + } + + void Lfx::WaitAndBeginFrame() { + if (m_winelfx_WaitAndBeginFrame) + m_winelfx_WaitAndBeginFrame(); + } + + void Lfx::SetTargetFrameTime(uint64_t frame_time_ns) { + if (m_winelfx_SetTargetFrameTime) + m_winelfx_SetTargetFrameTime(static_cast<__int64>(frame_time_ns)); + } +} \ No newline at end of file diff --git a/src/d3d/lfx.h b/src/d3d/lfx.h new file mode 100644 index 00000000..db590041 --- /dev/null +++ b/src/d3d/lfx.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +namespace dxvk { + class Lfx { + public: + Lfx(); + virtual ~Lfx(); + + [[nodiscard]] virtual bool IsAvailable() const; + virtual void WaitAndBeginFrame(); + virtual void SetTargetFrameTime(uint64_t frame_time_ns); + + private: + HMODULE m_lfxModule{}; + bool m_enabled = false; + + typedef void (*PFN_winelfx_WaitAndBeginFrame)(); + typedef void (*PFN_winelfx_SetTargetFrameTime)(__int64); + + PFN_winelfx_WaitAndBeginFrame m_winelfx_WaitAndBeginFrame{}; + PFN_winelfx_SetTargetFrameTime m_winelfx_SetTargetFrameTime{}; + }; +} \ No newline at end of file diff --git a/src/d3d/nvapi_d3d_instance.cpp b/src/d3d/nvapi_d3d_instance.cpp new file mode 100644 index 00000000..54b20d4b --- /dev/null +++ b/src/d3d/nvapi_d3d_instance.cpp @@ -0,0 +1,39 @@ +#include "../util/util_log.h" +#include "nvapi_d3d_instance.h" + +namespace dxvk { + NvapiD3dInstance::NvapiD3dInstance(ResourceFactory &resourceFactory) + : m_resourceFactory(resourceFactory) { + + } + + NvapiD3dInstance::~NvapiD3dInstance() = default; + + void NvapiD3dInstance::Initialize() { + m_lfx = m_resourceFactory.CreateLfx(); + if (m_lfx->IsAvailable()) + log::write("LatencyFleX loaded and initialized successfully"); + } + + bool NvapiD3dInstance::IsReflexAvailable() { + return m_lfx->IsAvailable(); + } + + bool NvapiD3dInstance::IsReflexEnabled() const { + return m_isLfxEnabled; + } + + void NvapiD3dInstance::SetReflexEnabled(bool value) { + m_isLfxEnabled = value; + } + + void NvapiD3dInstance::Sleep() { + if(IsReflexAvailable() && m_isLfxEnabled) + m_lfx->WaitAndBeginFrame(); + } + + void NvapiD3dInstance::SetTargetFrameTime(uint64_t frameTimeUs) { + constexpr uint64_t kNanoInMicro = 1000; + m_lfx->SetTargetFrameTime(frameTimeUs * kNanoInMicro); + } +} \ No newline at end of file diff --git a/src/d3d/nvapi_d3d_instance.h b/src/d3d/nvapi_d3d_instance.h new file mode 100644 index 00000000..60f54599 --- /dev/null +++ b/src/d3d/nvapi_d3d_instance.h @@ -0,0 +1,23 @@ +#pragma once + +#include "../resource_factory.h" + +namespace dxvk { + class NvapiD3dInstance { + public: + explicit NvapiD3dInstance(ResourceFactory &resourceFactory); + ~NvapiD3dInstance(); + + void Initialize(); + [[nodiscard]] bool IsReflexAvailable(); + [[nodiscard]] bool IsReflexEnabled() const; + void SetReflexEnabled(bool value); + void Sleep(); + void SetTargetFrameTime(uint64_t frameTimeUs); + + private: + ResourceFactory& m_resourceFactory; + std::unique_ptr m_lfx; + bool m_isLfxEnabled = false; + }; +} \ No newline at end of file diff --git a/src/meson.build b/src/meson.build index 6b477dae..e4648fe0 100644 --- a/src/meson.build +++ b/src/meson.build @@ -9,7 +9,9 @@ nvapi_src = files([ 'sysinfo/nvapi_output.cpp', 'sysinfo/nvapi_adapter.cpp', 'sysinfo/nvapi_adapter_registry.cpp', - 'sysinfo/resource_factory.cpp', + 'resource_factory.cpp', + 'd3d/lfx.cpp', + 'd3d/nvapi_d3d_instance.cpp', 'd3d11/nvapi_d3d11_device.cpp', 'd3d12/nvapi_d3d12_device.cpp', 'nvapi_interface.cpp', diff --git a/src/nvapi.cpp b/src/nvapi.cpp index cb55c78d..e276966c 100644 --- a/src/nvapi.cpp +++ b/src/nvapi.cpp @@ -264,6 +264,9 @@ extern "C" { return NvidiaDeviceNotFound(n); } + nvapiD3dInstance = std::make_unique(*resourceFactory); + nvapiD3dInstance->Initialize(); + return Ok(n); } } diff --git a/src/nvapi_d3d.cpp b/src/nvapi_d3d.cpp index 3994b3d1..22fb2e13 100644 --- a/src/nvapi_d3d.cpp +++ b/src/nvapi_d3d.cpp @@ -1,4 +1,5 @@ #include "nvapi_private.h" +#include "nvapi_static.h" #include "util/util_statuscode.h" extern "C" { @@ -102,4 +103,59 @@ extern "C" { return Ok(n, alreadyLoggedOk); } + + NvAPI_Status __cdecl NvAPI_D3D_Sleep(IUnknown *pDevice) { + constexpr auto n = __func__; + static bool alreadyLoggedOk = false; + static bool alreadyLoggedUnavailable = false; + + if (pDevice == nullptr) + return InvalidArgument(n); + + if (!nvapiD3dInstance->IsReflexAvailable()) + return NoImplementation(n, alreadyLoggedUnavailable); + + nvapiD3dInstance->Sleep(); + + return Ok(n, alreadyLoggedOk); + } + + NvAPI_Status __cdecl NvAPI_D3D_SetSleepMode(IUnknown *pDevice, + NV_SET_SLEEP_MODE_PARAMS *pSetSleepModeParams) { + constexpr auto n = __func__; + static bool alreadyLoggedUnavailable = false; + + if (pDevice == nullptr) + return InvalidArgument(n); + + if (pSetSleepModeParams->version != NV_SET_SLEEP_MODE_PARAMS_VER1) + return IncompatibleStructVersion(n); + + if (!nvapiD3dInstance->IsReflexAvailable()) + return NoImplementation(n, alreadyLoggedUnavailable); + + nvapiD3dInstance->SetReflexEnabled(pSetSleepModeParams->bLowLatencyMode); + if (pSetSleepModeParams->bLowLatencyMode) { + log::write(str::format(n, ": interval ", pSetSleepModeParams->minimumIntervalUs, " us")); + nvapiD3dInstance->SetTargetFrameTime(pSetSleepModeParams->minimumIntervalUs); + } + + auto f = str::format(n, " (", pSetSleepModeParams->bLowLatencyMode ? "Enable" : "Disable", ")"); + return Ok(f); + } + + NvAPI_Status __cdecl NvAPI_D3D_GetSleepStatus(IUnknown *pDevice, + NV_GET_SLEEP_STATUS_PARAMS *pGetSleepStatusParams) { + constexpr auto n = __func__; + static bool alreadyLoggedUnavailable = false; + + if (pGetSleepStatusParams->version != NV_GET_SLEEP_STATUS_PARAMS_VER1) + return IncompatibleStructVersion(n); + + if (!nvapiD3dInstance->IsReflexAvailable()) + return NoImplementation(n, alreadyLoggedUnavailable); + + pGetSleepStatusParams->bLowLatencyMode = nvapiD3dInstance->IsReflexEnabled(); + return Ok(n); + } } diff --git a/src/nvapi_interface.cpp b/src/nvapi_interface.cpp index 80f6e4ee..fc84a706 100644 --- a/src/nvapi_interface.cpp +++ b/src/nvapi_interface.cpp @@ -73,6 +73,9 @@ extern "C" { INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D_ImplicitSLIControl) INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D_BeginResourceRendering) INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D_EndResourceRendering) + INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D_SetSleepMode) + INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D_GetSleepStatus) + INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D_Sleep) INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_GPU_GetGPUType) INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_GPU_GetPCIIdentifiers) INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_GPU_GetFullName) diff --git a/src/nvapi_static.h b/src/nvapi_static.h index c172780a..4ed6c8ba 100644 --- a/src/nvapi_static.h +++ b/src/nvapi_static.h @@ -1,10 +1,12 @@ #pragma once -#include "sysinfo/resource_factory.h" +#include "resource_factory.h" #include "sysinfo/nvapi_adapter_registry.h" +#include "d3d/nvapi_d3d_instance.h" static std::unique_ptr resourceFactory; static std::unique_ptr nvapiAdapterRegistry; +static std::unique_ptr nvapiD3dInstance; static auto initializationMutex = std::mutex{}; static auto initializationCount = 0ULL; diff --git a/src/sysinfo/resource_factory.cpp b/src/resource_factory.cpp similarity index 85% rename from src/sysinfo/resource_factory.cpp rename to src/resource_factory.cpp index b7ae390a..61e09a12 100644 --- a/src/sysinfo/resource_factory.cpp +++ b/src/resource_factory.cpp @@ -20,4 +20,8 @@ namespace dxvk { std::unique_ptr ResourceFactory::CreateNvml() { return std::make_unique(); } + + std::unique_ptr ResourceFactory::CreateLfx() { + return std::make_unique(); + } } diff --git a/src/sysinfo/resource_factory.h b/src/resource_factory.h similarity index 63% rename from src/sysinfo/resource_factory.h rename to src/resource_factory.h index 79a2efc1..f7f1d5ea 100644 --- a/src/sysinfo/resource_factory.h +++ b/src/resource_factory.h @@ -1,9 +1,10 @@ #pragma once -#include "../nvapi_private.h" -#include "../util/com_pointer.h" -#include "vulkan.h" -#include "nvml.h" +#include "nvapi_private.h" +#include "util/com_pointer.h" +#include "sysinfo/vulkan.h" +#include "sysinfo/nvml.h" +#include "d3d/lfx.h" namespace dxvk { class ResourceFactory { @@ -15,5 +16,6 @@ namespace dxvk { virtual Com CreateDXGIFactory1(); virtual std::unique_ptr CreateVulkan(); virtual std::unique_ptr CreateNvml(); + virtual std::unique_ptr CreateLfx(); }; } diff --git a/src/sysinfo/nvapi_adapter_registry.h b/src/sysinfo/nvapi_adapter_registry.h index e5d8ceb7..b99590f0 100644 --- a/src/sysinfo/nvapi_adapter_registry.h +++ b/src/sysinfo/nvapi_adapter_registry.h @@ -1,7 +1,7 @@ #pragma once #include "../nvapi_private.h" -#include "resource_factory.h" +#include "../resource_factory.h" #include "nvapi_adapter.h" #include "nvapi_output.h" #include "vulkan.h" diff --git a/tests/meson.build b/tests/meson.build index cbeb9fe9..fa0b6332 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -6,10 +6,12 @@ nvapi_src = files([ '../src/util/util_log.cpp', '../src/sysinfo/vulkan.cpp', '../src/sysinfo/nvml.cpp', + '../src/d3d/lfx.cpp', + '../src/d3d/nvapi_d3d_instance.cpp', '../src/sysinfo/nvapi_output.cpp', '../src/sysinfo/nvapi_adapter.cpp', '../src/sysinfo/nvapi_adapter_registry.cpp', - '../src/sysinfo/resource_factory.cpp', + '../src/resource_factory.cpp', '../src/d3d11/nvapi_d3d11_device.cpp', '../src/d3d12/nvapi_d3d12_device.cpp', ]) diff --git a/tests/mock_factory.cpp b/tests/mock_factory.cpp index 259815c9..60ccfc05 100644 --- a/tests/mock_factory.cpp +++ b/tests/mock_factory.cpp @@ -1,8 +1,10 @@ class MockFactory : public ResourceFactory { - public: - MockFactory(std::unique_ptr dxgiFactory1Mock, std::unique_ptr vulkanMock, std::unique_ptr nvmlMock) - : m_dxgiFactoryMock(std::move(dxgiFactory1Mock)), m_vulkanMock(std::move(vulkanMock)), m_nvmlMock(std::move(nvmlMock)) {}; +public: + MockFactory(std::unique_ptr dxgiFactory1Mock, std::unique_ptr vulkanMock, + std::unique_ptr nvmlMock, std::unique_ptr lfxMock) + : m_dxgiFactoryMock(std::move(dxgiFactory1Mock)), m_vulkanMock(std::move(vulkanMock)), + m_nvmlMock(std::move(nvmlMock)), m_lfxMock(std::move(lfxMock)) {}; Com CreateDXGIFactory1() override { Com dxgiFactory = m_dxgiFactoryMock.get(); @@ -17,8 +19,13 @@ class MockFactory : public ResourceFactory { return std::move(m_nvmlMock); } - private: + std::unique_ptr CreateLfx() override { + return std::move(m_lfxMock); + } + +private: std::unique_ptr m_dxgiFactoryMock; std::unique_ptr m_vulkanMock; std::unique_ptr m_nvmlMock; + std::unique_ptr m_lfxMock; }; diff --git a/tests/nvapi_d3d.cpp b/tests/nvapi_d3d.cpp index ce438639..0b4acd91 100644 --- a/tests/nvapi_d3d.cpp +++ b/tests/nvapi_d3d.cpp @@ -93,3 +93,61 @@ TEST_CASE("D3D methods succeed", "[.d3d]") { } } } +TEST_CASE("Reflex methods", "[.d3d]"){ + UnknownMock unknown; + auto dxgiFactory = std::make_unique(); + auto vulkan = std::make_unique(); + auto nvml = std::make_unique(); + auto lfx = std::make_unique(); + DXGIDxvkAdapterMock adapter; + DXGIOutputMock output; + + auto e = ConfigureDefaultTestEnvironment(*dxgiFactory, *vulkan, *nvml, *lfx, adapter, output); + + SECTION("Enabling Reflex does not return OK when LFX is unavailable") { + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); + REQUIRE(NvAPI_Initialize() == NVAPI_OK); + + NV_SET_SLEEP_MODE_PARAMS params {}; + params.version = NV_SET_SLEEP_MODE_PARAMS_VER; + params.bLowLatencyMode = true; + REQUIRE(NvAPI_D3D_SetSleepMode(&unknown, ¶ms) != NVAPI_OK); + } + + SECTION("Reflex methods work when LFX is available") { + ALLOW_CALL(*lfx, IsAvailable()).RETURN(true); // NOLINT(bugprone-use-after-move) + + SECTION("GetSleepStatus returns OK") { + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); + REQUIRE(NvAPI_Initialize() == NVAPI_OK); + + NV_GET_SLEEP_STATUS_PARAMS params{}; + params.version = NV_GET_SLEEP_STATUS_PARAMS_VER; + REQUIRE(NvAPI_D3D_GetSleepStatus(&unknown, ¶ms) == NVAPI_OK); + } + + SECTION("SetSleepMode returns OK") { + REQUIRE_CALL(*lfx, SetTargetFrameTime(UINT64_C(0))); // NOLINT(bugprone-use-after-move) + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); + REQUIRE(NvAPI_Initialize() == NVAPI_OK); + + NV_SET_SLEEP_MODE_PARAMS params{}; + params.version = NV_SET_SLEEP_MODE_PARAMS_VER; + params.bLowLatencyMode = true; + REQUIRE(NvAPI_D3D_SetSleepMode(&unknown, ¶ms) == NVAPI_OK); + } + + SECTION("Sleep calls LFX throttle callback and returns OK") { + REQUIRE_CALL(*lfx, SetTargetFrameTime(UINT64_C(0))); // NOLINT(bugprone-use-after-move) + REQUIRE_CALL(*lfx, WaitAndBeginFrame()); + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); + REQUIRE(NvAPI_Initialize() == NVAPI_OK); + + NV_SET_SLEEP_MODE_PARAMS params{}; + params.version = NV_SET_SLEEP_MODE_PARAMS_VER; + params.bLowLatencyMode = true; + REQUIRE(NvAPI_D3D_SetSleepMode(&unknown, ¶ms) == NVAPI_OK); + REQUIRE(NvAPI_D3D_Sleep(&unknown) == NVAPI_OK); + } + } +} diff --git a/tests/nvapi_d3d12.cpp b/tests/nvapi_d3d12.cpp index 40d6233d..9f0ab087 100644 --- a/tests/nvapi_d3d12.cpp +++ b/tests/nvapi_d3d12.cpp @@ -116,11 +116,12 @@ TEST_CASE("D3D12 methods succeed", "[.d3d12]") { auto dxgiFactory = std::make_unique(); auto vulkan = std::make_unique(); auto nvml = std::make_unique(); + auto lfx = std::make_unique(); DXGIDxvkAdapterMock adapter; DXGIOutputMock output; auto luid = new LUID{}; - auto e = ConfigureDefaultTestEnvironment(*dxgiFactory, *vulkan, *nvml, adapter, output); + auto e = ConfigureDefaultTestEnvironment(*dxgiFactory, *vulkan, *nvml, *lfx, adapter, output); ALLOW_CALL(device, QueryInterface(__uuidof(ID3D12Device), _)) .LR_SIDE_EFFECT(*_2 = static_cast(&device)) @@ -137,7 +138,7 @@ TEST_CASE("D3D12 methods succeed", "[.d3d12]") { .LR_RETURN(luid); SECTION("GetGraphicsCapabilities without matching adapter returns OK with sm_0") { - SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml)); + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); REQUIRE(NvAPI_Initialize() == NVAPI_OK); NV_D3D12_GRAPHICS_CAPS graphicsCaps{}; @@ -178,7 +179,7 @@ TEST_CASE("D3D12 methods succeed", "[.d3d12]") { fragmentShadingRateProps->primitiveFragmentShadingRateWithMultipleViewports = VK_TRUE; })); - SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml)); + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); REQUIRE(NvAPI_Initialize() == NVAPI_OK); NV_D3D12_GRAPHICS_CAPS graphicsCaps; diff --git a/tests/nvapi_d3d_mocks.cpp b/tests/nvapi_d3d_mocks.cpp index 9941cf65..cbb4ffa6 100644 --- a/tests/nvapi_d3d_mocks.cpp +++ b/tests/nvapi_d3d_mocks.cpp @@ -5,3 +5,9 @@ class UnknownMock : public mock_interface { MAKE_MOCK0 (AddRef, ULONG(), override); MAKE_MOCK0 (Release, ULONG(), override); }; + +class LfxMock : public mock_interface { + IMPLEMENT_CONST_MOCK0 (IsAvailable); + IMPLEMENT_MOCK0 (WaitAndBeginFrame); + IMPLEMENT_MOCK1 (SetTargetFrameTime); +}; \ No newline at end of file diff --git a/tests/nvapi_sysinfo.cpp b/tests/nvapi_sysinfo.cpp index 1907c72e..99f2614e 100644 --- a/tests/nvapi_sysinfo.cpp +++ b/tests/nvapi_sysinfo.cpp @@ -16,13 +16,14 @@ TEST_CASE("Initialize succeeds", "[.sysinfo]") { auto dxgiFactory = std::make_unique(); auto vulkan = std::make_unique(); auto nvml = std::make_unique(); + auto lfx = std::make_unique(); DXGIDxvkAdapterMock adapter; DXGIOutputMock output; - auto e = ConfigureDefaultTestEnvironment(*dxgiFactory, *vulkan, *nvml, adapter, output); + auto e = ConfigureDefaultTestEnvironment(*dxgiFactory, *vulkan, *nvml, *lfx, adapter, output); SECTION("Initialize returns OK") { - SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml)); + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); REQUIRE(NvAPI_Initialize() == NVAPI_OK); REQUIRE(NvAPI_Unload() == NVAPI_OK); } @@ -31,7 +32,7 @@ TEST_CASE("Initialize succeeds", "[.sysinfo]") { ALLOW_CALL(*dxgiFactory, EnumAdapters1(_, _)) // NOLINT(bugprone-use-after-move) .RETURN(DXGI_ERROR_NOT_FOUND); - SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml)); + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); REQUIRE(NvAPI_Initialize() == NVAPI_NVIDIA_DEVICE_NOT_FOUND); REQUIRE(NvAPI_Unload() == NVAPI_API_NOT_INITIALIZED); } @@ -44,7 +45,7 @@ TEST_CASE("Initialize succeeds", "[.sysinfo]") { driverProps->driverID = VK_DRIVER_ID_MESA_RADV; })); - SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml)); + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); REQUIRE(NvAPI_Initialize() == NVAPI_NVIDIA_DEVICE_NOT_FOUND); REQUIRE(NvAPI_Unload() == NVAPI_API_NOT_INITIALIZED); } @@ -54,15 +55,16 @@ TEST_CASE("Topology methods succeed", "[.sysinfo]") { auto dxgiFactory = std::make_unique(); auto vulkan = std::make_unique(); auto nvml = std::make_unique(); + auto lfx = std::make_unique(); DXGIDxvkAdapterMock adapter1; DXGIDxvkAdapterMock adapter2; DXGIOutputMock output1; DXGIOutputMock output2; DXGIOutputMock output3; - auto e = ConfigureExtendedTestEnvironment(*dxgiFactory, *vulkan, *nvml, adapter1, adapter2, output1, output2, output3); + auto e = ConfigureExtendedTestEnvironment(*dxgiFactory, *vulkan, *nvml, *lfx, adapter1, adapter2, output1, output2, output3); - SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml)); + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); REQUIRE(NvAPI_Initialize() == NVAPI_OK); SECTION("EnumLogicalGPUs succeeds") { @@ -249,15 +251,16 @@ TEST_CASE("Sysinfo methods succeed", "[.sysinfo]") { auto dxgiFactory = std::make_unique(); auto vulkan = std::make_unique(); auto nvml = std::make_unique(); + auto lfx = std::make_unique(); DXGIDxvkAdapterMock adapter; DXGIOutputMock output; - auto e = ConfigureDefaultTestEnvironment(*dxgiFactory, *vulkan, *nvml, adapter, output); + auto e = ConfigureDefaultTestEnvironment(*dxgiFactory, *vulkan, *nvml, *lfx, adapter, output); ::SetEnvironmentVariableA("DXVK_NVAPI_DRIVER_VERSION", ""); SECTION("Initialize and unloads return OK") { - SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml)); + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); REQUIRE(NvAPI_Initialize() == NVAPI_OK); REQUIRE(NvAPI_Unload() == NVAPI_OK); } @@ -271,7 +274,7 @@ TEST_CASE("Sysinfo methods succeed", "[.sysinfo]") { props->driverVersion = (470 << 22) | (35 << 14) | 1 << 6; })); - SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml)); + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); REQUIRE(NvAPI_Initialize() == NVAPI_OK); NvU32 version; @@ -302,7 +305,7 @@ TEST_CASE("Sysinfo methods succeed", "[.sysinfo]") { props->driverVersion = (args.major << 22) | (args.minor << 12) | args.patch; })); - SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml)); + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); REQUIRE(NvAPI_Initialize() == NVAPI_OK); NvDisplayHandle handle; @@ -339,7 +342,7 @@ TEST_CASE("Sysinfo methods succeed", "[.sysinfo]") { props->driverVersion = (470 << 22) | (45 << 14) | (0 << 6); })); - SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml)); + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); REQUIRE(NvAPI_Initialize() == NVAPI_OK); NvDisplayHandle handle; @@ -366,7 +369,7 @@ TEST_CASE("Sysinfo methods succeed", "[.sysinfo]") { }) ); - SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml)); + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); REQUIRE(NvAPI_Initialize() == NVAPI_OK); NvPhysicalGpuHandle handle; @@ -405,7 +408,7 @@ TEST_CASE("Sysinfo methods succeed", "[.sysinfo]") { fragmentShadingRateProps->primitiveFragmentShadingRateWithMultipleViewports = VK_TRUE; })); - SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml)); + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); REQUIRE(NvAPI_Initialize() == NVAPI_OK); NvPhysicalGpuHandle handle; @@ -444,7 +447,7 @@ TEST_CASE("Sysinfo methods succeed", "[.sysinfo]") { props->deviceType = VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU; })); - SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml)); + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); REQUIRE(NvAPI_Initialize() == NVAPI_OK); NvPhysicalGpuHandle handle; @@ -465,7 +468,7 @@ TEST_CASE("Sysinfo methods succeed", "[.sysinfo]") { props->deviceID = 0x1234; })); - SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml)); + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); REQUIRE(NvAPI_Initialize() == NVAPI_OK); NvPhysicalGpuHandle handle; @@ -489,7 +492,7 @@ TEST_CASE("Sysinfo methods succeed", "[.sysinfo]") { strcpy(props->deviceName, name); })); - SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml)); + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); REQUIRE(NvAPI_Initialize() == NVAPI_OK); NvPhysicalGpuHandle handle; @@ -512,7 +515,7 @@ TEST_CASE("Sysinfo methods succeed", "[.sysinfo]") { pciBusInfoProps->pciBus = id; })); - SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml)); + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); REQUIRE(NvAPI_Initialize() == NVAPI_OK); NvPhysicalGpuHandle handle; @@ -535,7 +538,7 @@ TEST_CASE("Sysinfo methods succeed", "[.sysinfo]") { pciBusInfoProps->pciDevice = id; })); - SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml)); + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); REQUIRE(NvAPI_Initialize() == NVAPI_OK); NvPhysicalGpuHandle handle; @@ -563,7 +566,7 @@ TEST_CASE("Sysinfo methods succeed", "[.sysinfo]") { driverProps->driverID = VK_DRIVER_ID_NVIDIA_PROPRIETARY; })); - SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml)); + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); REQUIRE(NvAPI_Initialize() == NVAPI_OK); NvPhysicalGpuHandle handle; @@ -582,7 +585,7 @@ TEST_CASE("Sysinfo methods succeed", "[.sysinfo]") { _3->memoryProperties.memoryHeaps[0].size = 8191 * 1024; }); - SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml)); + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); REQUIRE(NvAPI_Initialize() == NVAPI_OK); NvPhysicalGpuHandle handle; @@ -604,7 +607,7 @@ TEST_CASE("Sysinfo methods succeed", "[.sysinfo]") { driverProps->driverID = VK_DRIVER_ID_NVIDIA_PROPRIETARY; })); - SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml)); + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); REQUIRE(NvAPI_Initialize() == NVAPI_OK); NvPhysicalGpuHandle handle; @@ -639,7 +642,7 @@ TEST_CASE("Sysinfo methods succeed", "[.sysinfo]") { fragmentShadingRateProps->primitiveFragmentShadingRateWithMultipleViewports = VK_TRUE; })); - SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml)); + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); REQUIRE(NvAPI_Initialize() == NVAPI_OK); NvPhysicalGpuHandle handle; @@ -683,7 +686,7 @@ TEST_CASE("Sysinfo methods succeed", "[.sysinfo]") { driverProps->driverID = VK_DRIVER_ID_MESA_RADV; })); - SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml)); + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); REQUIRE(NvAPI_Initialize() == NVAPI_OK); NvPhysicalGpuHandle handle; @@ -708,7 +711,7 @@ TEST_CASE("Sysinfo methods succeed", "[.sysinfo]") { .LR_SIDE_EFFECT(_2->pciSubSystemId = id) .RETURN(NVML_SUCCESS); - SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml)); + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); REQUIRE(NvAPI_Initialize() == NVAPI_OK); NvPhysicalGpuHandle handle; @@ -725,7 +728,7 @@ TEST_CASE("Sysinfo methods succeed", "[.sysinfo]") { .LR_SIDE_EFFECT(strcpy(_2, version)) .RETURN(NVML_SUCCESS); - SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml)); + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); REQUIRE(NvAPI_Initialize() == NVAPI_OK); NvPhysicalGpuHandle handle; @@ -746,7 +749,7 @@ TEST_CASE("Sysinfo methods succeed", "[.sysinfo]") { }) .RETURN(NVML_SUCCESS); - SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml)); + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); REQUIRE(NvAPI_Initialize() == NVAPI_OK); NvPhysicalGpuHandle handle; @@ -774,7 +777,7 @@ TEST_CASE("Sysinfo methods succeed", "[.sysinfo]") { .LR_SIDE_EFFECT(*_3 = temp) .RETURN(NVML_SUCCESS); - SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml)); + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); REQUIRE(NvAPI_Initialize() == NVAPI_OK); NvPhysicalGpuHandle handle; @@ -816,7 +819,7 @@ TEST_CASE("Sysinfo methods succeed", "[.sysinfo]") { .LR_SIDE_EFFECT(*_2 = NVML_PSTATE_2) .RETURN(NVML_SUCCESS); - SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml)); + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); REQUIRE(NvAPI_Initialize() == NVAPI_OK); NvPhysicalGpuHandle handle; @@ -841,7 +844,7 @@ TEST_CASE("Sysinfo methods succeed", "[.sysinfo]") { .LR_SIDE_EFFECT(*_3 = videoClock) .RETURN(NVML_SUCCESS); - SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml)); + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); REQUIRE(NvAPI_Initialize() == NVAPI_OK); NvPhysicalGpuHandle handle; @@ -924,7 +927,7 @@ TEST_CASE("Sysinfo methods succeed", "[.sysinfo]") { ALLOW_CALL(*nvml, IsAvailable()) // NOLINT(bugprone-use-after-move) .RETURN(false); - SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml)); + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); REQUIRE(NvAPI_Initialize() == NVAPI_OK); NvPhysicalGpuHandle handle; @@ -960,7 +963,7 @@ TEST_CASE("Sysinfo methods succeed", "[.sysinfo]") { ALLOW_CALL(*nvml, ErrorString(_)) .RETURN("error"); - SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml)); + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); REQUIRE(NvAPI_Initialize() == NVAPI_OK); NvPhysicalGpuHandle handle; diff --git a/tests/nvapi_tests.cpp b/tests/nvapi_tests.cpp index 71198bdd..20c50883 100644 --- a/tests/nvapi_tests.cpp +++ b/tests/nvapi_tests.cpp @@ -14,8 +14,8 @@ #include "../src/nvapi_mosaic.cpp" #include "nvapi_sysinfo_mocks.cpp" -#include "nvapi_sysinfo_util.cpp" #include "nvapi_d3d_mocks.cpp" +#include "resource_factory_util.cpp" #include "nvapi_d3d11_mocks.cpp" #include "nvapi_d3d12_mocks.cpp" diff --git a/tests/nvapi_sysinfo_util.cpp b/tests/resource_factory_util.cpp similarity index 95% rename from tests/nvapi_sysinfo_util.cpp rename to tests/resource_factory_util.cpp index 731ed846..ebcc3588 100644 --- a/tests/nvapi_sysinfo_util.cpp +++ b/tests/resource_factory_util.cpp @@ -9,16 +9,18 @@ void ResetResourceFactory() { void SetupResourceFactory( std::unique_ptr dxgiFactory, std::unique_ptr vulkan, - std::unique_ptr nvml) { - resourceFactory = std::make_unique(std::move(dxgiFactory), std::move(vulkan), std::move(nvml)); + std::unique_ptr nvml, + std::unique_ptr lfx) { + resourceFactory = std::make_unique(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); nvapiAdapterRegistry.reset(); initializationCount = 0ULL; } -[[nodiscard]] std::array, 16> ConfigureDefaultTestEnvironment( +[[nodiscard]] std::array, 17> ConfigureDefaultTestEnvironment( DXGIFactory1Mock& dxgiFactory, VulkanMock& vulkan, NvmlMock& nvml, + LfxMock& lfx, DXGIDxvkAdapterMock& adapter, DXGIOutputMock& output) { return { @@ -64,14 +66,17 @@ void SetupResourceFactory( NAMED_ALLOW_CALL(vulkan, GetPhysicalDeviceMemoryProperties2(_, _, _)), NAMED_ALLOW_CALL(nvml, IsAvailable()) + .RETURN(false), + NAMED_ALLOW_CALL(lfx, IsAvailable()) .RETURN(false) }; } -[[nodiscard]] std::array, 28> ConfigureExtendedTestEnvironment( +[[nodiscard]] std::array, 29> ConfigureExtendedTestEnvironment( DXGIFactory1Mock& dxgiFactory, VulkanMock& vulkan, NvmlMock& nvml, + LfxMock& lfx, DXGIDxvkAdapterMock& adapter1, DXGIDxvkAdapterMock& adapter2, DXGIOutputMock& output1, @@ -159,6 +164,8 @@ void SetupResourceFactory( NAMED_ALLOW_CALL(vulkan, GetPhysicalDeviceMemoryProperties2(_, _, _)), NAMED_ALLOW_CALL(nvml, IsAvailable()) + .RETURN(false), + NAMED_ALLOW_CALL(lfx, IsAvailable()) .RETURN(false) }; } From 3e3ee257fe2bcdce905ca64e42c8e26e6f94dd43 Mon Sep 17 00:00:00 2001 From: Tatsuyuki Ishi Date: Tue, 8 Feb 2022 15:45:28 +0900 Subject: [PATCH 2/3] nvapi: Reflex support through LatencyFleX (v3 changes) --- src/d3d/lfx.cpp | 40 ++++++++++++++++++++++++---------- src/d3d/lfx.h | 8 +++---- src/d3d/nvapi_d3d_instance.cpp | 6 ++--- tests/nvapi_d3d.cpp | 34 ++++++++++++++++++++++------- tests/nvapi_d3d12.cpp | 2 +- 5 files changed, 61 insertions(+), 29 deletions(-) diff --git a/src/d3d/lfx.cpp b/src/d3d/lfx.cpp index fced8629..59e6437b 100644 --- a/src/d3d/lfx.cpp +++ b/src/d3d/lfx.cpp @@ -6,18 +6,34 @@ namespace dxvk { Lfx::Lfx() { - const auto lfxModuleName = "latencyflex_wine.dll"; + const auto lfxModuleName = "latencyflex_layer.dll"; + const auto lfxModuleNameFallback = "latencyflex_wine.dll"; m_lfxModule = ::LoadLibraryA(lfxModuleName); - if (m_lfxModule == nullptr) { + if (m_lfxModule) { + m_lfx_WaitAndBeginFrame = reinterpret_cast(GetProcAddress(m_lfxModule, + "lfx_WaitAndBeginFrame")); + m_lfx_SetTargetFrameTime = reinterpret_cast(GetProcAddress(m_lfxModule, + "lfx_SetTargetFrameTime")); + } else { auto lastError = ::GetLastError(); - if (lastError != ERROR_MOD_NOT_FOUND) // Ignore library not found + if (lastError != ERROR_MOD_NOT_FOUND) { log::write(str::format("Loading ", lfxModuleName, " failed with error code: ", lastError)); - - return; + } else { + // Try fallback entrypoints. These were used by an older version of LatencyFleX. + m_lfxModule = ::LoadLibraryA(lfxModuleNameFallback); + if (m_lfxModule) { + m_lfx_WaitAndBeginFrame = reinterpret_cast(GetProcAddress(m_lfxModule, + "winelfx_WaitAndBeginFrame")); + m_lfx_SetTargetFrameTime = reinterpret_cast(GetProcAddress(m_lfxModule, + "winelfx_SetTargetFrameTime")); + } else { + lastError = ::GetLastError(); + if (lastError != ERROR_MOD_NOT_FOUND) // Ignore library not found + log::write( + str::format("Loading ", lfxModuleNameFallback, " failed with error code: ", lastError)); + } + } } - - m_winelfx_WaitAndBeginFrame = reinterpret_cast(reinterpret_cast(GetProcAddress(m_lfxModule, "winelfx_WaitAndBeginFrame"))); - m_winelfx_SetTargetFrameTime = reinterpret_cast(reinterpret_cast(GetProcAddress(m_lfxModule, "winelfx_SetTargetFrameTime"))); } Lfx::~Lfx() { @@ -31,12 +47,12 @@ namespace dxvk { } void Lfx::WaitAndBeginFrame() { - if (m_winelfx_WaitAndBeginFrame) - m_winelfx_WaitAndBeginFrame(); + if (m_lfx_WaitAndBeginFrame) + m_lfx_WaitAndBeginFrame(); } void Lfx::SetTargetFrameTime(uint64_t frame_time_ns) { - if (m_winelfx_SetTargetFrameTime) - m_winelfx_SetTargetFrameTime(static_cast<__int64>(frame_time_ns)); + if (m_lfx_SetTargetFrameTime) + m_lfx_SetTargetFrameTime(static_cast<__int64>(frame_time_ns)); } } \ No newline at end of file diff --git a/src/d3d/lfx.h b/src/d3d/lfx.h index db590041..08a7bf7b 100644 --- a/src/d3d/lfx.h +++ b/src/d3d/lfx.h @@ -17,10 +17,10 @@ namespace dxvk { HMODULE m_lfxModule{}; bool m_enabled = false; - typedef void (*PFN_winelfx_WaitAndBeginFrame)(); - typedef void (*PFN_winelfx_SetTargetFrameTime)(__int64); + typedef void (*PFN_lfx_WaitAndBeginFrame)(); + typedef void (*PFN_lfx_SetTargetFrameTime)(__int64); - PFN_winelfx_WaitAndBeginFrame m_winelfx_WaitAndBeginFrame{}; - PFN_winelfx_SetTargetFrameTime m_winelfx_SetTargetFrameTime{}; + PFN_lfx_WaitAndBeginFrame m_lfx_WaitAndBeginFrame{}; + PFN_lfx_SetTargetFrameTime m_lfx_SetTargetFrameTime{}; }; } \ No newline at end of file diff --git a/src/d3d/nvapi_d3d_instance.cpp b/src/d3d/nvapi_d3d_instance.cpp index 54b20d4b..bd4cd954 100644 --- a/src/d3d/nvapi_d3d_instance.cpp +++ b/src/d3d/nvapi_d3d_instance.cpp @@ -3,9 +3,7 @@ namespace dxvk { NvapiD3dInstance::NvapiD3dInstance(ResourceFactory &resourceFactory) - : m_resourceFactory(resourceFactory) { - - } + : m_resourceFactory(resourceFactory) {} NvapiD3dInstance::~NvapiD3dInstance() = default; @@ -28,7 +26,7 @@ namespace dxvk { } void NvapiD3dInstance::Sleep() { - if(IsReflexAvailable() && m_isLfxEnabled) + if (m_isLfxEnabled) m_lfx->WaitAndBeginFrame(); } diff --git a/tests/nvapi_d3d.cpp b/tests/nvapi_d3d.cpp index 0b4acd91..177b6965 100644 --- a/tests/nvapi_d3d.cpp +++ b/tests/nvapi_d3d.cpp @@ -104,14 +104,32 @@ TEST_CASE("Reflex methods", "[.d3d]"){ auto e = ConfigureDefaultTestEnvironment(*dxgiFactory, *vulkan, *nvml, *lfx, adapter, output); - SECTION("Enabling Reflex does not return OK when LFX is unavailable") { - SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); - REQUIRE(NvAPI_Initialize() == NVAPI_OK); - - NV_SET_SLEEP_MODE_PARAMS params {}; - params.version = NV_SET_SLEEP_MODE_PARAMS_VER; - params.bLowLatencyMode = true; - REQUIRE(NvAPI_D3D_SetSleepMode(&unknown, ¶ms) != NVAPI_OK); + SECTION("LatencyFleX-depending methods return NoImplementation when LFX is unavailable") { + SECTION("GetSleepStatus returns NoImplementation") { + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); + REQUIRE(NvAPI_Initialize() == NVAPI_OK); + + NV_GET_SLEEP_STATUS_PARAMS params{}; + params.version = NV_GET_SLEEP_STATUS_PARAMS_VER; + REQUIRE(NvAPI_D3D_GetSleepStatus(&unknown, ¶ms) == NVAPI_NO_IMPLEMENTATION); + } + + SECTION("SetSleepMode returns NoImplementation") { + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); + REQUIRE(NvAPI_Initialize() == NVAPI_OK); + + NV_SET_SLEEP_MODE_PARAMS params{}; + params.version = NV_SET_SLEEP_MODE_PARAMS_VER; + params.bLowLatencyMode = true; + REQUIRE(NvAPI_D3D_SetSleepMode(&unknown, ¶ms) == NVAPI_NO_IMPLEMENTATION); + } + + SECTION("Sleep returns NoImplementation") { + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); + REQUIRE(NvAPI_Initialize() == NVAPI_OK); + + REQUIRE(NvAPI_D3D_Sleep(&unknown) == NVAPI_NO_IMPLEMENTATION); + } } SECTION("Reflex methods work when LFX is available") { diff --git a/tests/nvapi_d3d12.cpp b/tests/nvapi_d3d12.cpp index 9f0ab087..5d3b1406 100644 --- a/tests/nvapi_d3d12.cpp +++ b/tests/nvapi_d3d12.cpp @@ -193,7 +193,7 @@ TEST_CASE("D3D12 methods succeed", "[.d3d12]") { } SECTION("GetGraphicsCapabilities with future struct version returns incompatible-struct-version") { - SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml)); + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); REQUIRE(NvAPI_Initialize() == NVAPI_OK); NV_D3D12_GRAPHICS_CAPS graphicsCaps{}; From aa0653e047152d47cdf7f74f2a68c5c7db1c40dd Mon Sep 17 00:00:00 2001 From: Tatsuyuki Ishi Date: Wed, 9 Feb 2022 15:50:17 +0900 Subject: [PATCH 3/3] nvapi: Reflex support through LatencyFleX (v4 changes) --- src/d3d/lfx.cpp | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/src/d3d/lfx.cpp b/src/d3d/lfx.cpp index 59e6437b..3c1ec525 100644 --- a/src/d3d/lfx.cpp +++ b/src/d3d/lfx.cpp @@ -8,32 +8,31 @@ namespace dxvk { Lfx::Lfx() { const auto lfxModuleName = "latencyflex_layer.dll"; const auto lfxModuleNameFallback = "latencyflex_wine.dll"; + auto useFallbackEntrypoints = false; + m_lfxModule = ::LoadLibraryA(lfxModuleName); - if (m_lfxModule) { - m_lfx_WaitAndBeginFrame = reinterpret_cast(GetProcAddress(m_lfxModule, - "lfx_WaitAndBeginFrame")); - m_lfx_SetTargetFrameTime = reinterpret_cast(GetProcAddress(m_lfxModule, - "lfx_SetTargetFrameTime")); - } else { + if (m_lfxModule == nullptr && ::GetLastError() == ERROR_MOD_NOT_FOUND) { + // Try fallback entrypoints. These were used by versions prior to [9c2836f]. + // The fallback logic can be removed once enough time has passed since the release. + // [9c2836f]: https://github.com/ishitatsuyuki/LatencyFleX/commit/9c2836faf14196190a915064b53c27e675e47960 + m_lfxModule = ::LoadLibraryA(lfxModuleNameFallback); + useFallbackEntrypoints = true; + } + + if (m_lfxModule == nullptr) { auto lastError = ::GetLastError(); - if (lastError != ERROR_MOD_NOT_FOUND) { - log::write(str::format("Loading ", lfxModuleName, " failed with error code: ", lastError)); - } else { - // Try fallback entrypoints. These were used by an older version of LatencyFleX. - m_lfxModule = ::LoadLibraryA(lfxModuleNameFallback); - if (m_lfxModule) { - m_lfx_WaitAndBeginFrame = reinterpret_cast(GetProcAddress(m_lfxModule, - "winelfx_WaitAndBeginFrame")); - m_lfx_SetTargetFrameTime = reinterpret_cast(GetProcAddress(m_lfxModule, - "winelfx_SetTargetFrameTime")); - } else { - lastError = ::GetLastError(); - if (lastError != ERROR_MOD_NOT_FOUND) // Ignore library not found - log::write( - str::format("Loading ", lfxModuleNameFallback, " failed with error code: ", lastError)); - } - } + if (lastError != ERROR_MOD_NOT_FOUND) // Ignore library not found + log::write(str::format("Loading ", !useFallbackEntrypoints ? lfxModuleName : lfxModuleNameFallback, + " failed with error code: ", lastError)); + return; } + + m_lfx_WaitAndBeginFrame = reinterpret_cast(reinterpret_cast( + GetProcAddress(m_lfxModule, + !useFallbackEntrypoints ? "lfx_WaitAndBeginFrame" : "winelfx_WaitAndBeginFrame"))); + m_lfx_SetTargetFrameTime = reinterpret_cast(reinterpret_cast( + GetProcAddress(m_lfxModule, + !useFallbackEntrypoints ? "lfx_SetTargetFrameTime" : "winelfx_SetTargetFrameTime"))); } Lfx::~Lfx() {