Skip to content

Commit

Permalink
nvapi: Reflex support through LatencyFleX
Browse files Browse the repository at this point in the history
  • Loading branch information
ishitatsuyuki committed Jan 22, 2022
1 parent 50c5957 commit dd26cbd
Show file tree
Hide file tree
Showing 23 changed files with 329 additions and 53 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
6 changes: 3 additions & 3 deletions inc/nvapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
//!
//!
Expand Down Expand Up @@ -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
//!
//!
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion package-release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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" \
Expand Down
42 changes: 42 additions & 0 deletions src/d3d/lfx.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#include "lfx.h"

#include <windows.h>
#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<PFN_winelfx_WaitAndBeginFrame>(reinterpret_cast<void*>(GetProcAddress(m_lfxModule,"winelfx_WaitAndBeginFrame")));
m_winelfx_SetTargetFrameTime = reinterpret_cast<PFN_winelfx_SetTargetFrameTime>(reinterpret_cast<void*>(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));
}
}
26 changes: 26 additions & 0 deletions src/d3d/lfx.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#pragma once

#include <minwindef.h>
#include <cstdint>

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{};
};
}
37 changes: 37 additions & 0 deletions src/d3d/nvapi_d3d_instance.cpp
Original file line number Diff line number Diff line change
@@ -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() {
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);
}
}
23 changes: 23 additions & 0 deletions src/d3d/nvapi_d3d_instance.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#pragma once

#include "../resource_factory.h"

namespace dxvk {
class NvapiD3dInstance {
public:
explicit NvapiD3dInstance(ResourceFactory &resourceFactory);
virtual ~NvapiD3dInstance();

virtual bool Initialize();
virtual bool IsReflexAvailable();
virtual bool IsReflexEnabled();
virtual void SetReflexEnabled(bool value);
virtual void Sleep();
virtual void SetTargetFrameTime(uint64_t frameTimeNs);

private:
ResourceFactory m_resourceFactory;
std::unique_ptr<Lfx> m_lfx;
bool m_isLfxEnabled = false;
};
}
4 changes: 3 additions & 1 deletion src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
7 changes: 7 additions & 0 deletions src/nvapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,13 @@ extern "C" {
return NvidiaDeviceNotFound(n);
}

nvapiD3dInstance = std::make_unique<NvapiD3dInstance>(*resourceFactory);
if (!nvapiD3dInstance->Initialize()) {
nvapiD3dInstance.reset();
--initializationCount;
return NvidiaDeviceNotFound(n);
}

return Ok(n);
}
}
44 changes: 44 additions & 0 deletions src/nvapi_d3d.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "nvapi_private.h"
#include "nvapi_static.h"
#include "util/util_statuscode.h"

extern "C" {
Expand Down Expand Up @@ -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);
}
}
3 changes: 3 additions & 0 deletions src/nvapi_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 3 additions & 1 deletion src/nvapi_static.h
Original file line number Diff line number Diff line change
@@ -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<dxvk::ResourceFactory> resourceFactory;
static std::unique_ptr<dxvk::NvapiAdapterRegistry> nvapiAdapterRegistry;
static std::unique_ptr<dxvk::NvapiD3dInstance> nvapiD3dInstance;

static auto initializationMutex = std::mutex{};
static auto initializationCount = 0ULL;
4 changes: 4 additions & 0 deletions src/sysinfo/resource_factory.cpp → src/resource_factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,8 @@ namespace dxvk {
std::unique_ptr<Nvml> ResourceFactory::CreateNvml() {
return std::make_unique<Nvml>();
}

std::unique_ptr <Lfx> ResourceFactory::CreateLfx() {
return std::make_unique<Lfx>();
}
}
10 changes: 6 additions & 4 deletions src/sysinfo/resource_factory.h → src/resource_factory.h
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -15,5 +16,6 @@ namespace dxvk {
virtual Com<IDXGIFactory1> CreateDXGIFactory1();
virtual std::unique_ptr<Vulkan> CreateVulkan();
virtual std::unique_ptr<Nvml> CreateNvml();
virtual std::unique_ptr<Lfx> CreateLfx();
};
}
2 changes: 1 addition & 1 deletion src/sysinfo/nvapi_adapter_registry.h
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
4 changes: 3 additions & 1 deletion tests/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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',
])
Expand Down
15 changes: 11 additions & 4 deletions tests/mock_factory.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
class MockFactory : public ResourceFactory {

public:
MockFactory(std::unique_ptr<IDXGIFactory1> dxgiFactory1Mock, std::unique_ptr<Vulkan> vulkanMock, std::unique_ptr<Nvml> nvmlMock)
: m_dxgiFactoryMock(std::move(dxgiFactory1Mock)), m_vulkanMock(std::move(vulkanMock)), m_nvmlMock(std::move(nvmlMock)) {};
public:
MockFactory(std::unique_ptr<IDXGIFactory1> dxgiFactory1Mock, std::unique_ptr<Vulkan> vulkanMock,
std::unique_ptr<Nvml> nvmlMock, std::unique_ptr<Lfx> lfxMock)
: m_dxgiFactoryMock(std::move(dxgiFactory1Mock)), m_vulkanMock(std::move(vulkanMock)),
m_nvmlMock(std::move(nvmlMock)), m_lfxMock(std::move(lfxMock)) {};

Com<IDXGIFactory1> CreateDXGIFactory1() override {
Com<IDXGIFactory1> dxgiFactory = m_dxgiFactoryMock.get();
Expand All @@ -17,8 +19,13 @@ class MockFactory : public ResourceFactory {
return std::move(m_nvmlMock);
}

private:
std::unique_ptr<Lfx> CreateLfx() override {
return std::move(m_lfxMock);
}

private:
std::unique_ptr<IDXGIFactory1> m_dxgiFactoryMock;
std::unique_ptr<Vulkan> m_vulkanMock;
std::unique_ptr<Nvml> m_nvmlMock;
std::unique_ptr<Lfx> m_lfxMock;
};
Loading

0 comments on commit dd26cbd

Please sign in to comment.