Skip to content

Commit 0738bfd

Browse files
esullivan-nvidiaejsullivan
authored andcommitted
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 0738bfd

20 files changed

+750
-47
lines changed

src/d3d/nvapi_d3d_instance.cpp

Lines changed: 28 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,40 @@ 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+
bool NvapiD3dInstance::SetReflexMode(IUnknown* device, bool enable, bool boost, uint32_t frameTimeUs) {
30+
bool result = true;
31+
32+
if (IsReflexAvailable(device))
33+
m_isLowLatencyEnabled = enable;
34+
35+
if (m_lfx->IsAvailable() && enable)
36+
m_lfx->SetTargetFrameTime(frameTimeUs * kNanoInMicro);
37+
else if (NvapiD3dLowLatencyDevice::SupportsLowLatency(device))
38+
result = NvapiD3dLowLatencyDevice::SetLatencySleepMode(device, enable, boost, frameTimeUs);
39+
40+
return result;
3141
}
3242

33-
void NvapiD3dInstance::SetTargetFrameTime(uint64_t frameTimeUs) {
34-
constexpr uint64_t kNanoInMicro = 1000;
35-
m_lfx->SetTargetFrameTime(frameTimeUs * kNanoInMicro);
43+
bool NvapiD3dInstance::Sleep(IUnknown* device) {
44+
bool result = true;
45+
46+
if (m_lfx->IsAvailable() && m_isLowLatencyEnabled)
47+
m_lfx->WaitAndBeginFrame();
48+
else if (NvapiD3dLowLatencyDevice::SupportsLowLatency(device))
49+
result = NvapiD3dLowLatencyDevice::LatencySleep(device);
50+
51+
return result;
3652
}
3753
}

src/d3d/nvapi_d3d_instance.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,17 @@ 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+
[[nodiscard]] bool SetReflexMode(IUnknown* device, bool enable, bool boost, uint32_t frameTimeUs);
17+
[[nodiscard]] bool Sleep(IUnknown* device);
1818

1919
private:
20+
constexpr static uint64_t kNanoInMicro = 1000;
21+
2022
ResourceFactory& m_resourceFactory;
2123
std::unique_ptr<Lfx> m_lfx;
22-
bool m_isLfxEnabled = false;
24+
bool m_isLowLatencyEnabled = false;
2325
};
2426
}
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* device) {
5+
auto d3dLowLatencyDevice = GetLowLatencyDevice(device);
6+
if (d3dLowLatencyDevice == nullptr)
7+
return false;
8+
9+
return d3dLowLatencyDevice->SupportsLowLatency();
10+
}
11+
12+
bool NvapiD3dLowLatencyDevice::LatencySleep(IUnknown* device) {
13+
auto d3dLowLatencyDevice = GetLowLatencyDevice(device);
14+
if (d3dLowLatencyDevice == nullptr)
15+
return false;
16+
17+
return SUCCEEDED(d3dLowLatencyDevice->LatencySleep());
18+
}
19+
20+
bool NvapiD3dLowLatencyDevice::SetLatencySleepMode(IUnknown* device, bool lowLatencyMode, bool lowLatencyBoost, uint32_t minimumIntervalUs) {
21+
auto d3dLowLatencyDevice = GetLowLatencyDevice(device);
22+
if (d3dLowLatencyDevice == nullptr)
23+
return false;
24+
25+
return SUCCEEDED(d3dLowLatencyDevice->SetLatencySleepMode(lowLatencyMode, lowLatencyBoost, minimumIntervalUs));
26+
}
27+
28+
bool NvapiD3dLowLatencyDevice::GetLatencyInfo(IUnknown* device, D3D_LATENCY_RESULTS* latencyResults) {
29+
auto d3dLowLatencyDevice = GetLowLatencyDevice(device);
30+
if (d3dLowLatencyDevice == nullptr)
31+
return false;
32+
33+
return SUCCEEDED(d3dLowLatencyDevice->GetLatencyInfo(latencyResults));
34+
}
35+
36+
bool NvapiD3dLowLatencyDevice::SetLatencyMarker(IUnknown* device, uint64_t frameID, uint32_t markerType) {
37+
auto d3dLowLatencyDevice = GetLowLatencyDevice(device);
38+
if (d3dLowLatencyDevice == nullptr)
39+
return false;
40+
41+
return SUCCEEDED(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* device);
11+
static bool LatencySleep(IUnknown* device);
12+
static bool SetLatencySleepMode(IUnknown* device, bool lowLatencyMode, bool lowLatencyBoost, uint32_t minimumIntervalUs);
13+
static bool GetLatencyInfo(IUnknown* device, D3D_LATENCY_RESULTS* latencyResults);
14+
static bool SetLatencyMarker(IUnknown* device, 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: 57 additions & 19 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"
@@ -106,64 +108,100 @@ extern "C" {
106108

107109
NvAPI_Status __cdecl NvAPI_D3D_Sleep(IUnknown* pDevice) {
108110
constexpr auto n = __func__;
111+
static bool alreadyLoggedNoReflex = false;
112+
static bool alreadyLoggedError = false;
109113
static bool alreadyLoggedOk = false;
110-
static bool alreadyLoggedNoLfx = false;
111114

112115
if (nvapiAdapterRegistry == nullptr)
113116
return ApiNotInitialized(n);
114117

115-
if (!nvapiD3dInstance->IsReflexAvailable())
116-
return NoImplementation(n, alreadyLoggedNoLfx);
118+
if (!nvapiD3dInstance->IsReflexAvailable(pDevice))
119+
return NoImplementation(n, alreadyLoggedNoReflex);
117120

118-
nvapiD3dInstance->Sleep();
121+
if (!nvapiD3dInstance->Sleep(pDevice))
122+
return Error(n, alreadyLoggedError);
119123

120124
return Ok(n, alreadyLoggedOk);
121125
}
122126

123127
NvAPI_Status __cdecl NvAPI_D3D_SetSleepMode(IUnknown* pDevice, NV_SET_SLEEP_MODE_PARAMS* pSetSleepModeParams) {
124128
constexpr auto n = __func__;
125-
static bool alreadyLoggedNoLfx = false;
129+
static bool alreadyLoggedNoReflex = false;
130+
static bool alreadyLoggedError = false;
126131

127132
if (nvapiAdapterRegistry == nullptr)
128133
return ApiNotInitialized(n);
129134

130135
if (pSetSleepModeParams->version != NV_SET_SLEEP_MODE_PARAMS_VER1)
131136
return IncompatibleStructVersion(n);
132137

133-
if (!nvapiD3dInstance->IsReflexAvailable())
134-
return NoImplementation(n, alreadyLoggedNoLfx);
138+
if (!nvapiD3dInstance->IsReflexAvailable(pDevice))
139+
return NoImplementation(n, alreadyLoggedNoReflex);
135140

136-
nvapiD3dInstance->SetReflexEnabled(pSetSleepModeParams->bLowLatencyMode);
137-
if (pSetSleepModeParams->bLowLatencyMode)
138-
nvapiD3dInstance->SetTargetFrameTime(pSetSleepModeParams->minimumIntervalUs);
141+
if (!nvapiD3dInstance->SetReflexMode(pDevice, pSetSleepModeParams->bLowLatencyMode, pSetSleepModeParams->bLowLatencyBoost, pSetSleepModeParams->minimumIntervalUs))
142+
return Error(n, alreadyLoggedError);
139143

140144
return Ok(str::format(n, " (", pSetSleepModeParams->bLowLatencyMode ? (str::format("Enabled/", pSetSleepModeParams->minimumIntervalUs, "us")) : "Disabled", ")"));
141145
}
142146

143147
NvAPI_Status __cdecl NvAPI_D3D_GetSleepStatus(IUnknown* pDevice, NV_GET_SLEEP_STATUS_PARAMS* pGetSleepStatusParams) {
144148
constexpr auto n = __func__;
145-
static bool alreadyLoggedNoLfx = false;
149+
static bool alreadyLoggedNoReflex = false;
150+
static bool alreadyLoggedOk = false;
146151

147152
if (nvapiAdapterRegistry == nullptr)
148153
return ApiNotInitialized(n);
149154

150155
if (pGetSleepStatusParams->version != NV_GET_SLEEP_STATUS_PARAMS_VER1)
151156
return IncompatibleStructVersion(n);
152157

153-
if (!nvapiD3dInstance->IsReflexAvailable())
154-
return NoImplementation(n, alreadyLoggedNoLfx);
158+
if (!nvapiD3dInstance->IsReflexAvailable(pDevice))
159+
return NoImplementation(n, alreadyLoggedNoReflex);
155160

156-
pGetSleepStatusParams->bLowLatencyMode = nvapiD3dInstance->IsReflexEnabled();
157-
return Ok(n);
161+
pGetSleepStatusParams->bLowLatencyMode = nvapiD3dInstance->IsLowLatencyEnabled();
162+
163+
return Ok(n, alreadyLoggedOk);
158164
}
159165

160166
NvAPI_Status __cdecl NvAPI_D3D_GetLatency(IUnknown* pDev, NV_LATENCY_RESULT_PARAMS* pGetLatencyParams) {
161-
static bool alreadyLogged = false;
162-
return NoImplementation(__func__, alreadyLogged);
167+
constexpr auto n = __func__;
168+
static bool alreadyLoggedNoImpl = false;
169+
static bool alreadyLoggedError = false;
170+
static bool alreadyLoggedOk = false;
171+
172+
if (nvapiAdapterRegistry == nullptr)
173+
return ApiNotInitialized(n);
174+
175+
if (pGetLatencyParams->version != NV_LATENCY_RESULT_PARAMS_VER1)
176+
return IncompatibleStructVersion(n);
177+
178+
if (nvapiD3dInstance->IsUsingLfx() || !NvapiD3dLowLatencyDevice::SupportsLowLatency(pDev))
179+
return NoImplementation(n, alreadyLoggedNoImpl);
180+
181+
if (!NvapiD3dLowLatencyDevice::GetLatencyInfo(pDev, reinterpret_cast<D3D_LATENCY_RESULTS*>(pGetLatencyParams)))
182+
return Error(n, alreadyLoggedError);
183+
184+
return Ok(n, alreadyLoggedOk);
163185
}
164186

165187
NvAPI_Status __cdecl NvAPI_D3D_SetLatencyMarker(IUnknown* pDev, NV_LATENCY_MARKER_PARAMS* pSetLatencyMarkerParams) {
166-
static bool alreadyLogged = false;
167-
return NoImplementation(__func__, alreadyLogged);
188+
constexpr auto n = __func__;
189+
static bool alreadyLoggedNoImpl = false;
190+
static bool alreadyLoggedError = false;
191+
static bool alreadyLoggedOk = false;
192+
193+
if (nvapiAdapterRegistry == nullptr)
194+
return ApiNotInitialized(n);
195+
196+
if (pSetLatencyMarkerParams->version != NV_LATENCY_MARKER_PARAMS_VER1)
197+
return IncompatibleStructVersion(n);
198+
199+
if (nvapiD3dInstance->IsUsingLfx() || !NvapiD3dLowLatencyDevice::SupportsLowLatency(pDev))
200+
return NoImplementation(n, alreadyLoggedNoImpl);
201+
202+
if (!NvapiD3dLowLatencyDevice::SetLatencyMarker(pDev, pSetLatencyMarkerParams->frameID, pSetLatencyMarkerParams->markerType))
203+
return Error(n, alreadyLoggedError);
204+
205+
return Ok(n, alreadyLoggedOk);
168206
}
169207
}

0 commit comments

Comments
 (0)