Skip to content

Commit a4a5e80

Browse files
nvapi: Add support for Reflex
The intent of this commit is to enable Reflex for all D3D11, and D3D12 titles using dxvk-nvapi. It does this through a new device interface called ID3DLowLatencyDevice. This interface will be implemented by ID3D12Device in vkd3d-proton, and ID3D11Device in dxvk. To provide compatibility with LatencyFleX this change will only use the ID3DLowLatencyDevice interface when LatencyFleX is not detected.
1 parent ed4338f commit a4a5e80

15 files changed

+315
-29
lines changed

src/d3d/nvapi_d3d_instance.cpp

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "../util/util_log.h"
2+
#include "nvapi_d3d_low_latency_device.h"
23
#include "nvapi_d3d_instance.h"
34

45
namespace dxvk {
@@ -13,25 +14,35 @@ namespace dxvk {
1314
log::write("LatencyFleX loaded and initialized successfully");
1415
}
1516

16-
bool NvapiD3dInstance::IsReflexAvailable() {
17-
return m_lfx->IsAvailable();
17+
bool NvapiD3dInstance::IsReflexAvailable(IUnknown* device) {
18+
return NvapiD3dLowLatencyDevice::SupportsLowLatency(device) || m_lfx->IsAvailable();
1819
}
1920

20-
bool NvapiD3dInstance::IsReflexEnabled() const {
21-
return m_isLfxEnabled;
21+
bool NvapiD3dInstance::IsLowLatencyEnabled() const {
22+
return m_isLowLatencyEnabled;
2223
}
2324

24-
void NvapiD3dInstance::SetReflexEnabled(bool value) {
25-
m_isLfxEnabled = value;
25+
bool NvapiD3dInstance::IsUsingLfx() const {
26+
return m_lfx->IsAvailable();
2627
}
2728

28-
void NvapiD3dInstance::Sleep() {
29-
if (m_isLfxEnabled)
30-
m_lfx->WaitAndBeginFrame();
29+
void NvapiD3dInstance::SetReflexMode(IUnknown* device, bool enable, bool boost, uint64_t frameTimeUs) {
30+
if (IsReflexAvailable(device)) {
31+
m_isLowLatencyEnabled = enable;
32+
}
33+
34+
if (m_lfx->IsAvailable() && enable) {
35+
constexpr uint64_t kNanoInMicro = 1000;
36+
m_lfx->SetTargetFrameTime(frameTimeUs * kNanoInMicro);
37+
} else if (NvapiD3dLowLatencyDevice::SupportsLowLatency(device)) {
38+
NvapiD3dLowLatencyDevice::SetLatencySleepMode(device, enable, boost, frameTimeUs);
39+
}
3140
}
3241

33-
void NvapiD3dInstance::SetTargetFrameTime(uint64_t frameTimeUs) {
34-
constexpr uint64_t kNanoInMicro = 1000;
35-
m_lfx->SetTargetFrameTime(frameTimeUs * kNanoInMicro);
42+
void NvapiD3dInstance::Sleep(IUnknown* device) {
43+
if (m_lfx->IsAvailable() && m_isLowLatencyEnabled)
44+
m_lfx->WaitAndBeginFrame();
45+
else if (NvapiD3dLowLatencyDevice::SupportsLowLatency(device))
46+
NvapiD3dLowLatencyDevice::LatencySleep(device);
3647
}
3748
}

src/d3d/nvapi_d3d_instance.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ namespace dxvk {
1010
~NvapiD3dInstance();
1111

1212
void Initialize();
13-
[[nodiscard]] bool IsReflexAvailable();
14-
[[nodiscard]] bool IsReflexEnabled() const;
15-
void SetReflexEnabled(bool value);
16-
void Sleep();
17-
void SetTargetFrameTime(uint64_t frameTimeUs);
13+
[[nodiscard]] bool IsReflexAvailable(IUnknown* device);
14+
[[nodiscard]] bool IsLowLatencyEnabled() const;
15+
[[nodiscard]] bool IsUsingLfx() const;
16+
void SetReflexMode(IUnknown* device, bool enable, bool boost, uint64_t frameTimeUs);
17+
void Sleep(IUnknown* device);
1818

1919
private:
2020
ResourceFactory& m_resourceFactory;
2121
std::unique_ptr<Lfx> m_lfx;
22-
bool m_isLfxEnabled = false;
22+
bool m_isLowLatencyEnabled = false;
2323
};
2424
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#include "nvapi_d3d_low_latency_device.h"
2+
3+
namespace dxvk {
4+
bool NvapiD3dLowLatencyDevice::SupportsLowLatency(IUnknown* pDevice) {
5+
auto d3dLowLatencyDevice = GetLowLatencyDevice(pDevice);
6+
if (d3dLowLatencyDevice == nullptr)
7+
return false;
8+
9+
return d3dLowLatencyDevice->SupportsLowLatency();
10+
}
11+
12+
bool NvapiD3dLowLatencyDevice::LatencySleep(IUnknown* pDevice) {
13+
auto d3dLowLatencyDevice = GetLowLatencyDevice(pDevice);
14+
if (d3dLowLatencyDevice == nullptr)
15+
return false;
16+
17+
return d3dLowLatencyDevice->LatencySleep();
18+
}
19+
20+
bool NvapiD3dLowLatencyDevice::SetLatencySleepMode(IUnknown* pDevice, bool lowLatencyMode, bool lowLatencyBoost, uint32_t minimumIntervalUs) {
21+
auto d3dLowLatencyDevice = GetLowLatencyDevice(pDevice);
22+
if (d3dLowLatencyDevice == nullptr)
23+
return false;
24+
25+
return d3dLowLatencyDevice->SetLatencySleepMode(lowLatencyMode, lowLatencyBoost, minimumIntervalUs);
26+
}
27+
28+
bool NvapiD3dLowLatencyDevice::GetLatencyInfo(IUnknown* pDevice, D3D_LATENCY_RESULTS* latency_results) {
29+
auto d3dLowLatencyDevice = GetLowLatencyDevice(pDevice);
30+
if (d3dLowLatencyDevice == nullptr)
31+
return false;
32+
33+
return d3dLowLatencyDevice->GetLatencyInfo(latency_results);
34+
}
35+
36+
bool NvapiD3dLowLatencyDevice::SetLatencyMarker(IUnknown* pDevice, uint64_t frameID, uint32_t markerType) {
37+
auto d3dLowLatencyDevice = GetLowLatencyDevice(pDevice);
38+
if (d3dLowLatencyDevice == nullptr)
39+
return false;
40+
41+
return d3dLowLatencyDevice->SetLatencyMarker(frameID, markerType);
42+
}
43+
44+
Com<ID3DLowLatencyDevice> NvapiD3dLowLatencyDevice::GetLowLatencyDevice(IUnknown* device) {
45+
std::scoped_lock lock(m_LowLatencyDeviceMutex);
46+
auto it = m_lowLatencyDeviceMap.find(device);
47+
if (it != m_lowLatencyDeviceMap.end())
48+
return it->second;
49+
50+
Com<ID3DLowLatencyDevice> d3dLowLatencyDevice;
51+
if (FAILED(device->QueryInterface(IID_PPV_ARGS(&d3dLowLatencyDevice))))
52+
return nullptr;
53+
54+
m_lowLatencyDeviceMap.emplace(device, d3dLowLatencyDevice.ptr());
55+
56+
return d3dLowLatencyDevice;
57+
}
58+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#pragma once
2+
3+
#include "../nvapi_private.h"
4+
#include "../shared/shared_interfaces.h"
5+
#include "../util/com_pointer.h"
6+
7+
namespace dxvk {
8+
class NvapiD3dLowLatencyDevice {
9+
public:
10+
static bool SupportsLowLatency(IUnknown* pDevice);
11+
static bool LatencySleep(IUnknown* pDevice);
12+
static bool SetLatencySleepMode(IUnknown* pDevice, bool lowLatencyMode, bool lowLatencyBoost, uint32_t minimumIntervalUs);
13+
static bool GetLatencyInfo(IUnknown* pDevice, D3D_LATENCY_RESULTS* latency_results);
14+
static bool SetLatencyMarker(IUnknown* pDevice, uint64_t frameID, uint32_t markerType);
15+
16+
private:
17+
inline static std::unordered_map<IUnknown*, ID3DLowLatencyDevice*> m_lowLatencyDeviceMap;
18+
19+
inline static std::mutex m_LowLatencyDeviceMutex;
20+
21+
[[nodiscard]] static Com<ID3DLowLatencyDevice> GetLowLatencyDevice(IUnknown* device);
22+
};
23+
}

src/d3d12/nvapi_d3d12_device.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,14 @@ namespace dxvk {
8181
return SUCCEEDED(cubinDevice->GetCudaSurfaceObject(uavHandle, reinterpret_cast<UINT32*>(cudaSurfaceHandle)));
8282
}
8383

84+
bool NvapiD3d12Device::NotifyOutOfBandCommandQueue(ID3D12CommandQueue* commandQueue, D3D12_OUT_OF_BAND_CQ_TYPE type) {
85+
auto commandQueueExt = GetCommandQueueExt(commandQueue);
86+
if (commandQueueExt == nullptr)
87+
return false;
88+
89+
return SUCCEEDED(commandQueueExt->NotifyOutOfBandCommandQueue(type));
90+
}
91+
8492
bool NvapiD3d12Device::LaunchCubinShader(ID3D12GraphicsCommandList* commandList, NVDX_ObjectHandle pShader, NvU32 blockX, NvU32 blockY, NvU32 blockZ, const void* params, NvU32 paramSize) {
8593
auto commandListExt = GetCommandListExt(commandList);
8694
if (!commandListExt.has_value())
@@ -146,6 +154,22 @@ namespace dxvk {
146154
return deviceExt;
147155
}
148156

157+
Com<ID3D12CommandQueueExt> NvapiD3d12Device::GetCommandQueueExt(ID3D12CommandQueue* commandQueue) {
158+
std::scoped_lock lock(m_commandQueueMutex);
159+
auto it = m_commandQueueMap.find(commandQueue);
160+
if (it != m_commandQueueMap.end())
161+
return it->second;
162+
163+
Com<ID3D12CommandQueueExt> commandQueueExt;
164+
if (FAILED(commandQueue->QueryInterface(IID_PPV_ARGS(&commandQueueExt))))
165+
return nullptr;
166+
167+
if (commandQueueExt != nullptr)
168+
m_commandQueueMap.emplace(commandQueue, commandQueueExt.ptr());
169+
170+
return commandQueueExt;
171+
}
172+
149173
std::optional<NvapiD3d12Device::CommandListExtWithVersion> NvapiD3d12Device::GetCommandListExt(ID3D12GraphicsCommandList* commandList) {
150174
std::scoped_lock lock(m_commandListMutex);
151175
auto it = m_commandListMap.find(commandList);
@@ -169,11 +193,13 @@ namespace dxvk {
169193
}
170194

171195
void NvapiD3d12Device::ClearCacheMaps() {
196+
std::scoped_lock commandQueueLock(m_commandQueueMutex);
172197
std::scoped_lock commandListLock(m_commandListMutex);
173198
std::scoped_lock cubinDeviceLock(m_cubinDeviceMutex);
174199
std::scoped_lock cubinSmemLock(m_cubinSmemMutex);
175200

176201
m_cubinDeviceMap.clear();
202+
m_commandQueueMap.clear();
177203
m_commandListMap.clear();
178204
m_cubinSmemMap.clear();
179205
}

src/d3d12/nvapi_d3d12_device.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ namespace dxvk {
2424
static bool DestroyCubinComputeShader(ID3D12Device* device, NVDX_ObjectHandle shader);
2525
static bool GetCudaTextureObject(ID3D12Device* device, D3D12_CPU_DESCRIPTOR_HANDLE srvHandle, D3D12_CPU_DESCRIPTOR_HANDLE samplerHandle, NvU32* cudaTextureHandle);
2626
static bool GetCudaSurfaceObject(ID3D12Device* device, D3D12_CPU_DESCRIPTOR_HANDLE uavHandle, NvU32* cudaSurfaceHandle);
27+
static bool NotifyOutOfBandCommandQueue(ID3D12CommandQueue* commandQueue, D3D12_OUT_OF_BAND_CQ_TYPE type);
2728
static bool LaunchCubinShader(ID3D12GraphicsCommandList* commandList, NVDX_ObjectHandle shader, NvU32 blockX, NvU32 blockY, NvU32 blockZ, const void* params, NvU32 paramSize);
2829
static bool CaptureUAVInfo(ID3D12Device* device, NVAPI_UAV_INFO* uavInfo);
2930
static bool IsFatbinPTXSupported(ID3D12Device* device);
@@ -32,15 +33,18 @@ namespace dxvk {
3233

3334
private:
3435
inline static std::unordered_map<ID3D12Device*, ID3D12DeviceExt*> m_cubinDeviceMap;
36+
inline static std::unordered_map<ID3D12CommandQueue*, ID3D12CommandQueueExt*> m_commandQueueMap;
3537
inline static std::unordered_map<ID3D12GraphicsCommandList*, CommandListExtWithVersion> m_commandListMap;
3638
inline static std::unordered_map<NVDX_ObjectHandle, NvU32> m_cubinSmemMap;
3739

3840
inline static std::mutex m_commandListMutex;
41+
inline static std::mutex m_commandQueueMutex;
3942
inline static std::mutex m_cubinDeviceMutex;
4043
inline static std::mutex m_cubinSmemMutex;
4144

4245
[[nodiscard]] static Com<ID3D12DeviceExt> GetCubinDevice(ID3D12Device* device);
4346
[[nodiscard]] static Com<ID3D12DeviceExt> GetDeviceExt(ID3D12Device* device, D3D12_VK_EXTENSION extension);
47+
[[nodiscard]] static Com<ID3D12CommandQueueExt> GetCommandQueueExt(ID3D12CommandQueue* commandQueue);
4448
[[nodiscard]] static std::optional<CommandListExtWithVersion> GetCommandListExt(ID3D12GraphicsCommandList* commandList);
4549
};
4650
}

src/meson.build

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
nvapi_src = files([
22
'dxvk/dxvk_interfaces.cpp',
33
'vkd3d-proton/vkd3d-proton_interfaces.cpp',
4+
'shared/shared_interfaces.cpp',
45
'util/util_string.cpp',
56
'util/util_env.cpp',
67
'util/util_log.cpp',
@@ -12,6 +13,7 @@ nvapi_src = files([
1213
'resource_factory.cpp',
1314
'd3d/lfx.cpp',
1415
'd3d/nvapi_d3d_instance.cpp',
16+
'd3d/nvapi_d3d_low_latency_device.cpp',
1517
'd3d11/nvapi_d3d11_device.cpp',
1618
'd3d12/nvapi_d3d12_device.cpp',
1719
'nvapi_globals.cpp',

src/nvapi_d3d.cpp

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#include "dxvk/dxvk_interfaces.h"
2+
#include "d3d/nvapi_d3d_low_latency_device.h"
13
#include "nvapi_private.h"
24
#include "nvapi_globals.h"
35
#include "util/util_statuscode.h"
@@ -112,10 +114,10 @@ extern "C" {
112114
if (nvapiAdapterRegistry == nullptr)
113115
return ApiNotInitialized(n);
114116

115-
if (!nvapiD3dInstance->IsReflexAvailable())
117+
if (!nvapiD3dInstance->IsReflexAvailable(pDevice))
116118
return NoImplementation(n, alreadyLoggedNoLfx);
117119

118-
nvapiD3dInstance->Sleep();
120+
nvapiD3dInstance->Sleep(pDevice);
119121

120122
return Ok(n, alreadyLoggedOk);
121123
}
@@ -130,12 +132,13 @@ extern "C" {
130132
if (pSetSleepModeParams->version != NV_SET_SLEEP_MODE_PARAMS_VER1)
131133
return IncompatibleStructVersion(n);
132134

133-
if (!nvapiD3dInstance->IsReflexAvailable())
135+
if (!nvapiD3dInstance->IsReflexAvailable(pDevice))
134136
return NoImplementation(n, alreadyLoggedNoLfx);
135137

136-
nvapiD3dInstance->SetReflexEnabled(pSetSleepModeParams->bLowLatencyMode);
137-
if (pSetSleepModeParams->bLowLatencyMode)
138-
nvapiD3dInstance->SetTargetFrameTime(pSetSleepModeParams->minimumIntervalUs);
138+
bool lowLatencyMode = pSetSleepModeParams->bLowLatencyMode;
139+
bool lowLatencyBoost = pSetSleepModeParams->bLowLatencyBoost;
140+
uint64_t minimumIntervalUs = pSetSleepModeParams->minimumIntervalUs;
141+
nvapiD3dInstance->SetReflexMode(pDevice, lowLatencyMode, lowLatencyBoost, minimumIntervalUs);
139142

140143
return Ok(str::format(n, " (", pSetSleepModeParams->bLowLatencyMode ? (str::format("Enabled/", pSetSleepModeParams->minimumIntervalUs, "us")) : "Disabled", ")"));
141144
}
@@ -150,20 +153,47 @@ extern "C" {
150153
if (pGetSleepStatusParams->version != NV_GET_SLEEP_STATUS_PARAMS_VER1)
151154
return IncompatibleStructVersion(n);
152155

153-
if (!nvapiD3dInstance->IsReflexAvailable())
156+
if (!nvapiD3dInstance->IsReflexAvailable(pDevice))
154157
return NoImplementation(n, alreadyLoggedNoLfx);
155158

156-
pGetSleepStatusParams->bLowLatencyMode = nvapiD3dInstance->IsReflexEnabled();
159+
pGetSleepStatusParams->bLowLatencyMode = nvapiD3dInstance->IsLowLatencyEnabled();
160+
157161
return Ok(n);
158162
}
159163

160164
NvAPI_Status __cdecl NvAPI_D3D_GetLatency(IUnknown* pDev, NV_LATENCY_RESULT_PARAMS* pGetLatencyParams) {
165+
constexpr auto n = __func__;
161166
static bool alreadyLogged = false;
162-
return NoImplementation(__func__, alreadyLogged);
167+
168+
if (nvapiAdapterRegistry == nullptr)
169+
return ApiNotInitialized(n);
170+
171+
if (pGetLatencyParams->version != NV_LATENCY_RESULT_PARAMS_VER1)
172+
return IncompatibleStructVersion(n);
173+
174+
if (nvapiD3dInstance->IsUsingLfx() || !NvapiD3dLowLatencyDevice::SupportsLowLatency(pDev))
175+
return NoImplementation(n, alreadyLogged);
176+
177+
NvapiD3dLowLatencyDevice::GetLatencyInfo(pDev, reinterpret_cast<D3D_LATENCY_RESULTS*>(pGetLatencyParams));
178+
179+
return Ok(n);
163180
}
164181

165182
NvAPI_Status __cdecl NvAPI_D3D_SetLatencyMarker(IUnknown* pDev, NV_LATENCY_MARKER_PARAMS* pSetLatencyMarkerParams) {
183+
constexpr auto n = __func__;
166184
static bool alreadyLogged = false;
167-
return NoImplementation(__func__, alreadyLogged);
185+
186+
if (nvapiAdapterRegistry == nullptr)
187+
return ApiNotInitialized(n);
188+
189+
if (pSetLatencyMarkerParams->version != NV_LATENCY_MARKER_PARAMS_VER1)
190+
return IncompatibleStructVersion(n);
191+
192+
if (nvapiD3dInstance->IsUsingLfx() || !NvapiD3dLowLatencyDevice::SupportsLowLatency(pDev))
193+
return NoImplementation(n, alreadyLogged);
194+
195+
NvapiD3dLowLatencyDevice::SetLatencyMarker(pDev, pSetLatencyMarkerParams->frameID, pSetLatencyMarkerParams->markerType);
196+
197+
return Ok(n);
168198
}
169199
}

0 commit comments

Comments
 (0)