From 46dea35c4e457fcf1f70964e6301ef3b0ccf2b97 Mon Sep 17 00:00:00 2001 From: Tatsuyuki Ishi Date: Fri, 24 Dec 2021 13:37:43 +0900 Subject: [PATCH] nvapi: Reflex support through LatencyFleX --- README.md | 1 + inc/nvapi.h | 6 +- package-release.sh | 2 +- src/d3d/lfx.cpp | 42 +++++++++++++ src/d3d/lfx.h | 26 ++++++++ src/d3d/nvapi_d3d_instance.cpp | 37 +++++++++++ src/d3d/nvapi_d3d_instance.h | 23 +++++++ src/meson.build | 4 +- src/nvapi.cpp | 7 +++ src/nvapi_d3d.cpp | 44 +++++++++++++ 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 | 57 +++++++++++++++++ tests/nvapi_d3d12.cpp | 7 ++- tests/nvapi_d3d_mocks.cpp | 6 ++ tests/nvapi_sysinfo.cpp | 61 ++++++++++--------- tests/nvapi_tests.cpp | 2 +- ...nfo_util.cpp => resource_factory_util.cpp} | 15 +++-- 23 files changed, 329 insertions(+), 53 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 f68feac5..737ba73d 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. - Several GPU topology related methods for adapter and display information, by querying from DXVK and Vulkan. diff --git a/inc/nvapi.h b/inc/nvapi.h index 382a4466..588e44eb 100755 --- a/inc/nvapi.h +++ b/inc/nvapi.h @@ -18251,7 +18251,7 @@ typedef NV_GET_SLEEP_STATUS_PARAMS_V1 NV_GET_SLEEP_STATUS_PARAMS; //! //! \since Release: 455 //! \param [in] pDev The target device. -//! \param [in] pGetSleepStatusParams Sleep status params. +//! \param [in] pGetSleepStatusParams WaitAndBeginFrame status params. //! SUPPORTED OS: Windows 7 and higher //! //! @@ -18307,7 +18307,7 @@ typedef NV_SET_SLEEP_MODE_PARAMS_V1 NV_SET_SLEEP_MODE_PARAMS; //! //! \since Release: 455 //! \param [in] pDev The target device. -//! \param [in] pSetSleepModeParams Sleep mode params. +//! \param [in] pSetSleepModeParams WaitAndBeginFrame mode params. //! SUPPORTED OS: Windows 7 and higher //! //! @@ -18451,7 +18451,7 @@ typedef NV_LATENCY_MARKER_PARAMS_V1 NV_LATENCY_MARKER_PARAMS; // //! DESCRIPTION: Set a latency marker to be tracked by the //! NvAPI_D3D_GetLatency function. SIMULATION_START must be the first marker -//! sent in a frame, after the previous frame's Sleep call (if used). +//! sent in a frame, after the previous frame's WaitAndBeginFrame call (if used). //! INPUT_SAMPLE may be sent to record the moment user input was sampled and //! should come between SIMULATION_START and SIMULATION_END. //! RENDERSUBMIT_START should come before any Direct3D calls are made for diff --git a/package-release.sh b/package-release.sh index 2c59e06b..fc92f562 100755 --- a/package-release.sh +++ b/package-release.sh @@ -66,7 +66,7 @@ function build_arch { cd "$SRC_DIR" meson --cross-file "$SRC_DIR/$crossfile$1.txt" \ - --buildtype "release" \ + --buildtype "debugoptimized" \ --prefix "$BUILD_DIR" \ --strip \ --bindir "x$1" \ 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..3d573c58 --- /dev/null +++ b/src/d3d/nvapi_d3d_instance.cpp @@ -0,0 +1,37 @@ +#include "nvapi_d3d_instance.h" + +namespace dxvk { + NvapiD3dInstance::NvapiD3dInstance(ResourceFactory &resourceFactory): m_resourceFactory(resourceFactory) { + + } + + NvapiD3dInstance::~NvapiD3dInstance() { + + } + + bool NvapiD3dInstance::Initialize() { + m_lfx = m_resourceFactory.CreateLfx(); + return true; + } + + 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 frameTimeNs) { + m_lfx->SetTargetFrameTime(frameTimeNs); + } +} \ 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..a22e0497 --- /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(); + + bool Initialize(); + bool IsReflexAvailable(); + [[nodiscard]] bool IsReflexEnabled() const; + void SetReflexEnabled(bool value); + void Sleep(); + void SetTargetFrameTime(uint64_t frameTimeNs); + + 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 5b6544c4..28142efc 100644 --- a/src/nvapi.cpp +++ b/src/nvapi.cpp @@ -223,6 +223,13 @@ extern "C" { return NvidiaDeviceNotFound(n); } + nvapiD3dInstance = std::make_unique(*resourceFactory); + if (!nvapiD3dInstance->Initialize()) { + nvapiD3dInstance.reset(); + --initializationCount; + return NvidiaDeviceNotFound(n); + } + return Ok(n); } } diff --git a/src/nvapi_d3d.cpp b/src/nvapi_d3d.cpp index 58bee635..c2d638de 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" { @@ -87,4 +88,47 @@ extern "C" { return Ok(n, alreadyLoggedOk); } + + NvAPI_Status __cdecl NvAPI_D3D_Sleep(IUnknown *pDevice) { + constexpr auto n = __func__; + static bool alreadyLogged = false; + if (!nvapiD3dInstance->IsReflexAvailable()) { + return NoImplementation(n, alreadyLogged); + } + nvapiD3dInstance->Sleep(); + return Ok(n, alreadyLogged); + } + + NvAPI_Status __cdecl NvAPI_D3D_SetSleepMode(IUnknown *pDevice, + NV_SET_SLEEP_MODE_PARAMS *pSetSleepModeParams) { + constexpr auto n = __func__; + if (pSetSleepModeParams->version != NV_SET_SLEEP_MODE_PARAMS_VER1) return IncompatibleStructVersion(n); + if (!nvapiD3dInstance->IsReflexAvailable()) { + log::write(str::format(n, ": Wine LFX module unavailable")); + return NoImplementation(); + } + auto f = str::format(n, " (", pSetSleepModeParams->bLowLatencyMode ? "Enable" : "Disable", ")"); + if (pSetSleepModeParams->bLowLatencyMode && !nvapiD3dInstance->IsReflexAvailable()) { + log::write(str::format(n, ": tried to enable Reflex but Wine LFX module is not available!")); + } + nvapiD3dInstance->SetReflexEnabled(pSetSleepModeParams->bLowLatencyMode); + if (pSetSleepModeParams->bLowLatencyMode) { + log::write(str::format(n, ": interval ", pSetSleepModeParams->minimumIntervalUs, " us")); + constexpr uint64_t kNanoInMicro = 1000; + nvapiD3dInstance->SetTargetFrameTime(pSetSleepModeParams->minimumIntervalUs * kNanoInMicro); + } + return Ok(f); + } + + NvAPI_Status __cdecl NvAPI_D3D_GetSleepStatus(IUnknown *pDevice, + NV_GET_SLEEP_STATUS_PARAMS *pGetSleepStatusParams) { + constexpr auto n = __func__; + if (pGetSleepStatusParams->version != NV_GET_SLEEP_STATUS_PARAMS_VER1) return IncompatibleStructVersion(n); + if (!nvapiD3dInstance->IsReflexAvailable()) { + log::write(str::format(n, ": Wine LFX module unavailable")); + return NoImplementation(); + } + pGetSleepStatusParams->bLowLatencyMode = nvapiD3dInstance->IsReflexEnabled(); + return Ok(n); + } } diff --git a/src/nvapi_interface.cpp b/src/nvapi_interface.cpp index 3b4b197b..52986997 100644 --- a/src/nvapi_interface.cpp +++ b/src/nvapi_interface.cpp @@ -72,6 +72,9 @@ extern "C" { INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D_GetCurrentSLIState) 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 46f69b6e..20920b38 100644 --- a/tests/nvapi_d3d.cpp +++ b/tests/nvapi_d3d.cpp @@ -56,3 +56,60 @@ TEST_CASE("D3D methods succeed", "[.d3d]") { REQUIRE(NvAPI_D3D1x_GetGraphicsCapabilities(&unknown, NV_D3D1x_GRAPHICS_CAPS_VER+1, reinterpret_cast(&caps)) == NVAPI_INCOMPATIBLE_STRUCT_VERSION); } } +TEST_CASE("Reflex method behavior", "[.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_V1 params {}; + params.version = NV_SET_SLEEP_MODE_PARAMS_VER1; + params.bLowLatencyMode = true; + REQUIRE(NvAPI_D3D_SetSleepMode(&unknown, ¶ms) != NVAPI_OK); + } + + SECTION("GetSleepStatus (V1) returns OK") { + ALLOW_CALL(*lfx, IsAvailable()).RETURN(true); // NOLINT(bugprone-use-after-move) + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); + REQUIRE(NvAPI_Initialize() == NVAPI_OK); + + NV_GET_SLEEP_STATUS_PARAMS_V1 params {}; + params.version = NV_GET_SLEEP_STATUS_PARAMS_VER1; + REQUIRE(NvAPI_D3D_GetSleepStatus(&unknown, ¶ms) == NVAPI_OK); + } + + SECTION("SetSleepMode returns OK") { + ALLOW_CALL(*lfx, IsAvailable()).RETURN(true); // NOLINT(bugprone-use-after-move) + ALLOW_CALL(*lfx, SetTargetFrameTime(UINT64_C(0))); + SetupResourceFactory(std::move(dxgiFactory), std::move(vulkan), std::move(nvml), std::move(lfx)); + REQUIRE(NvAPI_Initialize() == NVAPI_OK); + + NV_SET_SLEEP_MODE_PARAMS_V1 params {}; + params.version = NV_SET_SLEEP_MODE_PARAMS_VER1; + params.bLowLatencyMode = true; + REQUIRE(NvAPI_D3D_SetSleepMode(&unknown, ¶ms) == NVAPI_OK); + } + + SECTION("Sleep calls LFX bridge function") { + ALLOW_CALL(*lfx, IsAvailable()).RETURN(true); // NOLINT(bugprone-use-after-move) + ALLOW_CALL(*lfx, SetTargetFrameTime(UINT64_C(0))); + 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_V1 params {}; + params.version = NV_SET_SLEEP_MODE_PARAMS_VER1; + 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 24db1105..10c0678b 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("NvAPI_D3D12_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 e37666f7..3a541108 100644 --- a/tests/nvapi_sysinfo.cpp +++ b/tests/nvapi_sysinfo.cpp @@ -16,13 +16,14 @@ TEST_CASE("Initialize 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); 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 succeed", "[.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 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_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; @@ -362,7 +365,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; @@ -383,7 +386,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; @@ -407,7 +410,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; @@ -430,7 +433,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; @@ -453,7 +456,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; @@ -481,7 +484,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; @@ -500,7 +503,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; @@ -522,7 +525,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; @@ -557,7 +560,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; @@ -581,7 +584,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; @@ -606,7 +609,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; @@ -623,7 +626,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; @@ -644,7 +647,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; @@ -672,7 +675,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; @@ -694,7 +697,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; @@ -719,7 +722,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; @@ -742,7 +745,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; @@ -778,7 +781,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) }; }