From 53c17e911ecfaf5203c861c9fa2443c5c74ad7eb Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Mon, 20 Jan 2025 19:50:30 +0100 Subject: [PATCH] nvapi-d3d: Drop repeated frame IDs Frame IDs must be monotonic per marker type and device/command queue. --- src/nvapi/nvapi_d3d_low_latency_device.cpp | 21 +++++++++++-- src/nvapi/nvapi_d3d_low_latency_device.h | 5 +++- tests/nvapi_d3d.cpp | 34 ++++++++++++++++++++++ 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/src/nvapi/nvapi_d3d_low_latency_device.cpp b/src/nvapi/nvapi_d3d_low_latency_device.cpp index acaa731f..b5662431 100644 --- a/src/nvapi/nvapi_d3d_low_latency_device.cpp +++ b/src/nvapi/nvapi_d3d_low_latency_device.cpp @@ -5,7 +5,9 @@ namespace dxvk { LowLatencyFrameIdGenerator::LowLatencyFrameIdGenerator() : m_nextLowLatencyDeviceFrameId(1), - m_applicationIdList({0}) {} + m_applicationIdList({0}) { + m_lastFrameId.fill(std::numeric_limits::max()); + } LowLatencyFrameIdGenerator::~LowLatencyFrameIdGenerator() = default; @@ -42,6 +44,11 @@ namespace dxvk { return m_applicationIdList[((lowLatencyDeviceFrameId - 1) % applicationIdListSize)]; } + bool LowLatencyFrameIdGenerator::IsRepeatedFrame(uint64_t frameID, uint32_t markerType) { + // Should always be within bounds since we drop unsupported marker types in the entrypoints + return std::exchange(m_lastFrameId[markerType], frameID) == frameID; + } + bool NvapiD3dLowLatencyDevice::SupportsLowLatency(IUnknown* device) { auto d3dLowLatencyDevice = GetLowLatencyDevice(device); if (d3dLowLatencyDevice == nullptr) @@ -99,8 +106,12 @@ namespace dxvk { if (d3dLowLatencyDevice == nullptr) return false; + auto frameIdGenerator = GetFrameIdGenerator(d3dLowLatencyDevice.ptr()); + if (frameIdGenerator->IsRepeatedFrame(frameID, markerType)) + return true; // Silently drop repeated frame IDs + return SUCCEEDED(d3dLowLatencyDevice->SetLatencyMarker( - GetFrameIdGenerator(d3dLowLatencyDevice.ptr())->GetLowLatencyDeviceFrameId(frameID), markerType)); + frameIdGenerator->GetLowLatencyDeviceFrameId(frameID), markerType)); } bool NvapiD3dLowLatencyDevice::SetLatencyMarker(ID3D12CommandQueue* commandQueue, uint64_t frameID, uint32_t markerType) { @@ -108,8 +119,12 @@ namespace dxvk { if (d3dLowLatencyDevice == nullptr) return false; + auto frameIdGenerator = GetFrameIdGenerator(d3dLowLatencyDevice.ptr()); + if (frameIdGenerator->IsRepeatedFrame(frameID, markerType)) + return true; // Silently drop repeated frame IDs + return SUCCEEDED(d3dLowLatencyDevice->SetLatencyMarker( - GetFrameIdGenerator(d3dLowLatencyDevice.ptr())->GetLowLatencyDeviceFrameId(frameID), markerType)); + frameIdGenerator->GetLowLatencyDeviceFrameId(frameID), markerType)); } std::optional NvapiD3dLowLatencyDevice::ToMarkerType(NV_LATENCY_MARKER_TYPE markerType) { diff --git a/src/nvapi/nvapi_d3d_low_latency_device.h b/src/nvapi/nvapi_d3d_low_latency_device.h index 9e90dc96..1228f1fe 100644 --- a/src/nvapi/nvapi_d3d_low_latency_device.h +++ b/src/nvapi/nvapi_d3d_low_latency_device.h @@ -9,9 +9,10 @@ namespace dxvk { public: LowLatencyFrameIdGenerator(); virtual ~LowLatencyFrameIdGenerator(); - bool LowLatencyDeviceFrameIdInWindow(uint64_t lowLatencyDeviceFrameId) const; + [[nodiscard]] bool LowLatencyDeviceFrameIdInWindow(uint64_t lowLatencyDeviceFrameId) const; uint64_t GetLowLatencyDeviceFrameId(uint64_t applicationFrameId); uint64_t GetApplicationFrameId(uint64_t lowLatencyDeviceFrameId); + [[nodiscard]] bool IsRepeatedFrame(uint64_t frameID, uint32_t markerType); private: std::mutex m_frameIdGeneratorMutex; @@ -21,6 +22,8 @@ namespace dxvk { static constexpr uint32_t applicationIdListSize = 1000; std::array m_applicationIdList; + + std::array m_lastFrameId; }; class NvapiD3dLowLatencyDevice { diff --git a/tests/nvapi_d3d.cpp b/tests/nvapi_d3d.cpp index 6994e9a1..f6b8f562 100644 --- a/tests/nvapi_d3d.cpp +++ b/tests/nvapi_d3d.cpp @@ -415,6 +415,40 @@ TEST_CASE("D3D Reflex/LatencyFleX depending methods succeed", "[.d3d]") { REQUIRE(NvAPI_D3D_SetLatencyMarker(&unknown, &latencyMarkerParams) == NVAPI_OK); } + SECTION("SetLatencyMarker drops repeated frame IDs and returns OK") { + sequence seq1, seq2, seq3; + + REQUIRE_CALL(lowLatencyDevice, SetLatencyMarker(1ULL, VK_LATENCY_MARKER_PRESENT_START_NV)) + .IN_SEQUENCE(seq1) + .TIMES(1) + .RETURN(S_OK); + REQUIRE_CALL(lowLatencyDevice, SetLatencyMarker(2ULL, VK_LATENCY_MARKER_PRESENT_START_NV)) + .IN_SEQUENCE(seq2) + .TIMES(1) + .RETURN(S_OK); + REQUIRE_CALL(lowLatencyDevice, SetLatencyMarker(2ULL, VK_LATENCY_MARKER_PRESENT_END_NV)) + .IN_SEQUENCE(seq3) + .TIMES(1) + .RETURN(S_OK); + + SetupResourceFactory(std::move(dxgiFactory), std::move(vk), std::move(nvml), std::move(lfx)); + REQUIRE(NvAPI_Initialize() == NVAPI_OK); + + NV_LATENCY_MARKER_PARAMS latencyMarkerParams{}; + latencyMarkerParams.version = NV_LATENCY_MARKER_PARAMS_VER1; + latencyMarkerParams.frameID = 1; + latencyMarkerParams.markerType = PRESENT_START; + REQUIRE(NvAPI_D3D_SetLatencyMarker(&unknown, &latencyMarkerParams) == NVAPI_OK); + REQUIRE(NvAPI_D3D_SetLatencyMarker(&unknown, &latencyMarkerParams) == NVAPI_OK); + latencyMarkerParams.frameID = 2; + REQUIRE(NvAPI_D3D_SetLatencyMarker(&unknown, &latencyMarkerParams) == NVAPI_OK); + REQUIRE(NvAPI_D3D_SetLatencyMarker(&unknown, &latencyMarkerParams) == NVAPI_OK); + latencyMarkerParams.frameID = 2; + latencyMarkerParams.markerType = PRESENT_END; + REQUIRE(NvAPI_D3D_SetLatencyMarker(&unknown, &latencyMarkerParams) == NVAPI_OK); + REQUIRE(NvAPI_D3D_SetLatencyMarker(&unknown, &latencyMarkerParams) == NVAPI_OK); + } + SECTION("SetLatencyMarker correctly produces monotonic frame ids for a sequence of unique application frame ids") { REQUIRE_CALL(lowLatencyDevice, SetLatencySleepMode(true, false, 750U)) .RETURN(S_OK);