Skip to content

Commit

Permalink
nvapi: Add support for Reflex
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
esullivan-nvidia committed Oct 14, 2023
1 parent ed4338f commit 0bc516f
Show file tree
Hide file tree
Showing 14 changed files with 313 additions and 29 deletions.
35 changes: 23 additions & 12 deletions src/d3d/nvapi_d3d_instance.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "../util/util_log.h"
#include "nvapi_d3d_low_latency_device.h"
#include "nvapi_d3d_instance.h"

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

bool NvapiD3dInstance::IsReflexAvailable() {
return m_lfx->IsAvailable();
bool NvapiD3dInstance::IsReflexAvailable(IUnknown* device) {
return NvapiD3dLowLatencyDevice::SupportsLowLatency(device) || m_lfx->IsAvailable();
}

bool NvapiD3dInstance::IsReflexEnabled() const {
return m_isLfxEnabled;
bool NvapiD3dInstance::IsLowLatencyEnabled() const {
return m_isLowLatencyEnabled;
}

void NvapiD3dInstance::SetReflexEnabled(bool value) {
m_isLfxEnabled = value;
bool NvapiD3dInstance::IsUsingLfx() const {
return m_lfx->IsAvailable();
}

void NvapiD3dInstance::Sleep() {
if (m_isLfxEnabled)
m_lfx->WaitAndBeginFrame();
void NvapiD3dInstance::SetReflexMode(IUnknown* device, bool enable, bool boost, uint64_t frameTimeUs) {
if (IsReflexAvailable(device)) {
m_isLowLatencyEnabled = enable;
}

if (m_lfx->IsAvailable() && enable) {
constexpr uint64_t kNanoInMicro = 1000;
m_lfx->SetTargetFrameTime(frameTimeUs * kNanoInMicro);
} else if (NvapiD3dLowLatencyDevice::SupportsLowLatency(device)) {
NvapiD3dLowLatencyDevice::SetLatencySleepMode(device, enable, boost, frameTimeUs);
}
}

void NvapiD3dInstance::SetTargetFrameTime(uint64_t frameTimeUs) {
constexpr uint64_t kNanoInMicro = 1000;
m_lfx->SetTargetFrameTime(frameTimeUs * kNanoInMicro);
void NvapiD3dInstance::Sleep(IUnknown* device) {
if (m_lfx->IsAvailable() && m_isLowLatencyEnabled)
m_lfx->WaitAndBeginFrame();
else if (NvapiD3dLowLatencyDevice::SupportsLowLatency(device))
NvapiD3dLowLatencyDevice::LatencySleep(device);
}
}
12 changes: 6 additions & 6 deletions src/d3d/nvapi_d3d_instance.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ namespace dxvk {
~NvapiD3dInstance();

void Initialize();
[[nodiscard]] bool IsReflexAvailable();
[[nodiscard]] bool IsReflexEnabled() const;
void SetReflexEnabled(bool value);
void Sleep();
void SetTargetFrameTime(uint64_t frameTimeUs);
[[nodiscard]] bool IsReflexAvailable(IUnknown* device);
[[nodiscard]] bool IsLowLatencyEnabled() const;
[[nodiscard]] bool IsUsingLfx() const;
void SetReflexMode(IUnknown* device, bool enable, bool boost, uint64_t frameTimeUs);
void Sleep(IUnknown* device);

private:
ResourceFactory& m_resourceFactory;
std::unique_ptr<Lfx> m_lfx;
bool m_isLfxEnabled = false;
bool m_isLowLatencyEnabled = false;
};
}
58 changes: 58 additions & 0 deletions src/d3d/nvapi_d3d_low_latency_device.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#include "nvapi_d3d_low_latency_device.h"

namespace dxvk {
bool NvapiD3dLowLatencyDevice::SupportsLowLatency(IUnknown* pDevice) {
auto d3dLowLatencyDevice = GetLowLatencyDevice(pDevice);
if (d3dLowLatencyDevice == nullptr)
return false;

return d3dLowLatencyDevice->SupportsLowLatency();
}

bool NvapiD3dLowLatencyDevice::LatencySleep(IUnknown* pDevice) {
auto d3dLowLatencyDevice = GetLowLatencyDevice(pDevice);
if (d3dLowLatencyDevice == nullptr)
return false;

return d3dLowLatencyDevice->LatencySleep();
}

bool NvapiD3dLowLatencyDevice::SetLatencySleepMode(IUnknown* pDevice, bool lowLatencyMode, bool lowLatencyBoost, uint32_t minimumIntervalUs) {
auto d3dLowLatencyDevice = GetLowLatencyDevice(pDevice);
if (d3dLowLatencyDevice == nullptr)
return false;

return d3dLowLatencyDevice->SetLatencySleepMode(lowLatencyMode, lowLatencyBoost, minimumIntervalUs);
}

bool NvapiD3dLowLatencyDevice::GetLatencyInfo(IUnknown* pDevice, D3D_LATENCY_RESULTS* latency_results) {
auto d3dLowLatencyDevice = GetLowLatencyDevice(pDevice);
if (d3dLowLatencyDevice == nullptr)
return false;

return d3dLowLatencyDevice->GetLatencyInfo(latency_results);
}

bool NvapiD3dLowLatencyDevice::SetLatencyMarker(IUnknown* pDevice, uint64_t frameID, uint32_t markerType) {
auto d3dLowLatencyDevice = GetLowLatencyDevice(pDevice);
if (d3dLowLatencyDevice == nullptr)
return false;

return d3dLowLatencyDevice->SetLatencyMarker(frameID, markerType);
}

Com<ID3DLowLatencyDevice> NvapiD3dLowLatencyDevice::GetLowLatencyDevice(IUnknown* device) {
std::scoped_lock lock(m_LowLatencyDeviceMutex);
auto it = m_lowLatencyDeviceMap.find(device);
if (it != m_lowLatencyDeviceMap.end())
return it->second;

Com<ID3DLowLatencyDevice> d3dLowLatencyDevice;
if (FAILED(device->QueryInterface(IID_PPV_ARGS(&d3dLowLatencyDevice))))
return nullptr;

m_lowLatencyDeviceMap.emplace(device, d3dLowLatencyDevice.ptr());

return d3dLowLatencyDevice;
}
}
23 changes: 23 additions & 0 deletions src/d3d/nvapi_d3d_low_latency_device.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#pragma once

#include "../nvapi_private.h"
#include "../shared/shared_interfaces.h"
#include "../util/com_pointer.h"

namespace dxvk {
class NvapiD3dLowLatencyDevice {
public:
static bool SupportsLowLatency(IUnknown* pDevice);
static bool LatencySleep(IUnknown* pDevice);
static bool SetLatencySleepMode(IUnknown* pDevice, bool lowLatencyMode, bool lowLatencyBoost, uint32_t minimumIntervalUs);
static bool GetLatencyInfo(IUnknown* pDevice, D3D_LATENCY_RESULTS* latency_results);
static bool SetLatencyMarker(IUnknown* pDevice, uint64_t frameID, uint32_t markerType);

private:
inline static std::unordered_map<IUnknown*, ID3DLowLatencyDevice*> m_lowLatencyDeviceMap;

inline static std::mutex m_LowLatencyDeviceMutex;

[[nodiscard]] static Com<ID3DLowLatencyDevice> GetLowLatencyDevice(IUnknown* device);
};
}
26 changes: 26 additions & 0 deletions src/d3d12/nvapi_d3d12_device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,14 @@ namespace dxvk {
return SUCCEEDED(cubinDevice->GetCudaSurfaceObject(uavHandle, reinterpret_cast<UINT32*>(cudaSurfaceHandle)));
}

bool NvapiD3d12Device::NotifyOutOfBandCommandQueue(ID3D12CommandQueue* commandQueue, D3D12_OUT_OF_BAND_CQ_TYPE type) {
auto commandQueueExt = GetCommandQueueExt(commandQueue);
if (commandQueueExt == nullptr)
return false;

return SUCCEEDED(commandQueueExt->NotifyOutOfBandCommandQueue(type));
}

bool NvapiD3d12Device::LaunchCubinShader(ID3D12GraphicsCommandList* commandList, NVDX_ObjectHandle pShader, NvU32 blockX, NvU32 blockY, NvU32 blockZ, const void* params, NvU32 paramSize) {
auto commandListExt = GetCommandListExt(commandList);
if (!commandListExt.has_value())
Expand Down Expand Up @@ -146,6 +154,22 @@ namespace dxvk {
return deviceExt;
}

Com<ID3D12CommandQueueExt> NvapiD3d12Device::GetCommandQueueExt(ID3D12CommandQueue* commandQueue) {
std::scoped_lock lock(m_commandQueueMutex);
auto it = m_commandQueueMap.find(commandQueue);
if (it != m_commandQueueMap.end())
return it->second;

Com<ID3D12CommandQueueExt> commandQueueExt;
if (FAILED(commandQueue->QueryInterface(IID_PPV_ARGS(&commandQueueExt))))
return nullptr;

if (commandQueueExt != nullptr)
m_commandQueueMap.emplace(commandQueue, commandQueueExt.ptr());

return commandQueueExt;
}

std::optional<NvapiD3d12Device::CommandListExtWithVersion> NvapiD3d12Device::GetCommandListExt(ID3D12GraphicsCommandList* commandList) {
std::scoped_lock lock(m_commandListMutex);
auto it = m_commandListMap.find(commandList);
Expand All @@ -169,11 +193,13 @@ namespace dxvk {
}

void NvapiD3d12Device::ClearCacheMaps() {
std::scoped_lock commandQueueLock(m_commandQueueMutex);
std::scoped_lock commandListLock(m_commandListMutex);
std::scoped_lock cubinDeviceLock(m_cubinDeviceMutex);
std::scoped_lock cubinSmemLock(m_cubinSmemMutex);

m_cubinDeviceMap.clear();
m_commandQueueMap.clear();
m_commandListMap.clear();
m_cubinSmemMap.clear();
}
Expand Down
4 changes: 4 additions & 0 deletions src/d3d12/nvapi_d3d12_device.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ namespace dxvk {
static bool DestroyCubinComputeShader(ID3D12Device* device, NVDX_ObjectHandle shader);
static bool GetCudaTextureObject(ID3D12Device* device, D3D12_CPU_DESCRIPTOR_HANDLE srvHandle, D3D12_CPU_DESCRIPTOR_HANDLE samplerHandle, NvU32* cudaTextureHandle);
static bool GetCudaSurfaceObject(ID3D12Device* device, D3D12_CPU_DESCRIPTOR_HANDLE uavHandle, NvU32* cudaSurfaceHandle);
static bool NotifyOutOfBandCommandQueue(ID3D12CommandQueue* commandQueue, D3D12_OUT_OF_BAND_CQ_TYPE type);
static bool LaunchCubinShader(ID3D12GraphicsCommandList* commandList, NVDX_ObjectHandle shader, NvU32 blockX, NvU32 blockY, NvU32 blockZ, const void* params, NvU32 paramSize);
static bool CaptureUAVInfo(ID3D12Device* device, NVAPI_UAV_INFO* uavInfo);
static bool IsFatbinPTXSupported(ID3D12Device* device);
Expand All @@ -32,15 +33,18 @@ namespace dxvk {

private:
inline static std::unordered_map<ID3D12Device*, ID3D12DeviceExt*> m_cubinDeviceMap;
inline static std::unordered_map<ID3D12CommandQueue*, ID3D12CommandQueueExt*> m_commandQueueMap;
inline static std::unordered_map<ID3D12GraphicsCommandList*, CommandListExtWithVersion> m_commandListMap;
inline static std::unordered_map<NVDX_ObjectHandle, NvU32> m_cubinSmemMap;

inline static std::mutex m_commandListMutex;
inline static std::mutex m_commandQueueMutex;
inline static std::mutex m_cubinDeviceMutex;
inline static std::mutex m_cubinSmemMutex;

[[nodiscard]] static Com<ID3D12DeviceExt> GetCubinDevice(ID3D12Device* device);
[[nodiscard]] static Com<ID3D12DeviceExt> GetDeviceExt(ID3D12Device* device, D3D12_VK_EXTENSION extension);
[[nodiscard]] static Com<ID3D12CommandQueueExt> GetCommandQueueExt(ID3D12CommandQueue* commandQueue);
[[nodiscard]] static std::optional<CommandListExtWithVersion> GetCommandListExt(ID3D12GraphicsCommandList* commandList);
};
}
2 changes: 2 additions & 0 deletions src/meson.build
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
nvapi_src = files([
'dxvk/dxvk_interfaces.cpp',
'vkd3d-proton/vkd3d-proton_interfaces.cpp',
'shared/shared_interfaces.cpp',
'util/util_string.cpp',
'util/util_env.cpp',
'util/util_log.cpp',
Expand All @@ -12,6 +13,7 @@ nvapi_src = files([
'resource_factory.cpp',
'd3d/lfx.cpp',
'd3d/nvapi_d3d_instance.cpp',
'd3d/nvapi_d3d_low_latency_device.cpp',
'd3d11/nvapi_d3d11_device.cpp',
'd3d12/nvapi_d3d12_device.cpp',
'nvapi_globals.cpp',
Expand Down
50 changes: 40 additions & 10 deletions src/nvapi_d3d.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#include "dxvk/dxvk_interfaces.h"
#include "d3d/nvapi_d3d_low_latency_device.h"
#include "nvapi_private.h"
#include "nvapi_globals.h"
#include "util/util_statuscode.h"
Expand Down Expand Up @@ -112,10 +114,10 @@ extern "C" {
if (nvapiAdapterRegistry == nullptr)
return ApiNotInitialized(n);

if (!nvapiD3dInstance->IsReflexAvailable())
if (!nvapiD3dInstance->IsReflexAvailable(pDevice))
return NoImplementation(n, alreadyLoggedNoLfx);

nvapiD3dInstance->Sleep();
nvapiD3dInstance->Sleep(pDevice);

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

if (!nvapiD3dInstance->IsReflexAvailable())
if (!nvapiD3dInstance->IsReflexAvailable(pDevice))
return NoImplementation(n, alreadyLoggedNoLfx);

nvapiD3dInstance->SetReflexEnabled(pSetSleepModeParams->bLowLatencyMode);
if (pSetSleepModeParams->bLowLatencyMode)
nvapiD3dInstance->SetTargetFrameTime(pSetSleepModeParams->minimumIntervalUs);
bool lowLatencyMode = pSetSleepModeParams->bLowLatencyMode;
bool lowLatencyBoost = pSetSleepModeParams->bLowLatencyBoost;
uint64_t minimumIntervalUs = pSetSleepModeParams->minimumIntervalUs;
nvapiD3dInstance->SetReflexMode(pDevice, lowLatencyMode, lowLatencyBoost, minimumIntervalUs);

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

if (!nvapiD3dInstance->IsReflexAvailable())
if (!nvapiD3dInstance->IsReflexAvailable(pDevice))
return NoImplementation(n, alreadyLoggedNoLfx);

pGetSleepStatusParams->bLowLatencyMode = nvapiD3dInstance->IsReflexEnabled();
pGetSleepStatusParams->bLowLatencyMode = nvapiD3dInstance->IsLowLatencyEnabled();

return Ok(n);
}

NvAPI_Status __cdecl NvAPI_D3D_GetLatency(IUnknown* pDev, NV_LATENCY_RESULT_PARAMS* pGetLatencyParams) {
constexpr auto n = __func__;
static bool alreadyLogged = false;
return NoImplementation(__func__, alreadyLogged);

if (nvapiAdapterRegistry == nullptr)
return ApiNotInitialized(n);

if (pGetLatencyParams->version != NV_LATENCY_RESULT_PARAMS_VER1)
return IncompatibleStructVersion(n);

if (nvapiD3dInstance->IsUsingLfx() || !NvapiD3dLowLatencyDevice::SupportsLowLatency(pDev))
return NoImplementation(n, alreadyLogged);

NvapiD3dLowLatencyDevice::GetLatencyInfo(pDev, reinterpret_cast<D3D_LATENCY_RESULTS*>(pGetLatencyParams));

return Ok(n);
}

NvAPI_Status __cdecl NvAPI_D3D_SetLatencyMarker(IUnknown* pDev, NV_LATENCY_MARKER_PARAMS* pSetLatencyMarkerParams) {
constexpr auto n = __func__;
static bool alreadyLogged = false;
return NoImplementation(__func__, alreadyLogged);

if (nvapiAdapterRegistry == nullptr)
return ApiNotInitialized(n);

if (pSetLatencyMarkerParams->version != NV_LATENCY_MARKER_PARAMS_VER1)
return IncompatibleStructVersion(n);

if (nvapiD3dInstance->IsUsingLfx() || !NvapiD3dLowLatencyDevice::SupportsLowLatency(pDev))
return NoImplementation(n, alreadyLogged);

NvapiD3dLowLatencyDevice::SetLatencyMarker(pDev, pSetLatencyMarkerParams->frameID, pSetLatencyMarkerParams->markerType);

return Ok(n);
}
}
Loading

0 comments on commit 0bc516f

Please sign in to comment.