From 1e6950fd77ac3c23a37313efb9980209e7b74927 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 22 Jul 2024 14:55:36 +0200 Subject: [PATCH] rhi: d3d12: Also default to max frame latency 2 Follow what's been done in the D3D11 backend. Slightly different logic here and there due to always having the newer interfaces available, but does the same thing. Use QT_D3D_MAX_FRAME_LATENCY to override the default 2. [ChangeLog][RHI] The D3D12 backend creates swapchains from now on with DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT by default, with a max frame latency of 2. Task-number: QTBUG-127267 Change-Id: I4d0361ab546a1c0041592389f8954281f588b0ba Reviewed-by: Andy Nichols (cherry picked from commit 91cae3f9cc9cb97eab69e39d405000f2fd1e533a) Reviewed-by: Qt Cherry-pick Bot --- src/gui/rhi/qrhid3d12.cpp | 25 +++++++++++++++++++++++++ src/gui/rhi/qrhid3d12_p.h | 2 ++ 2 files changed, 27 insertions(+) diff --git a/src/gui/rhi/qrhid3d12.cpp b/src/gui/rhi/qrhid3d12.cpp index 0f176c683dc..2a178fbb9a0 100644 --- a/src/gui/rhi/qrhid3d12.cpp +++ b/src/gui/rhi/qrhid3d12.cpp @@ -223,6 +223,11 @@ bool QRhiD3D12::create(QRhi::Flags flags) } } + if (qEnvironmentVariableIsSet("QT_D3D_MAX_FRAME_LATENCY")) + maxFrameLatency = UINT(qMax(0, qEnvironmentVariableIntValue("QT_D3D_MAX_FRAME_LATENCY"))); + if (maxFrameLatency != 0) + qCDebug(QRHI_LOG_INFO, "Using frame latency waitable object with max frame latency %u", maxFrameLatency); + supportsAllowTearing = false; IDXGIFactory5 *factory5 = nullptr; if (SUCCEEDED(dxgiFactory->QueryInterface(__uuidof(IDXGIFactory5), reinterpret_cast(&factory5)))) { @@ -1527,6 +1532,9 @@ QRhi::FrameOpResult QRhiD3D12::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginF for (QD3D12SwapChain *sc : std::as_const(swapchains)) sc->waitCommandCompletionForFrameSlot(currentFrameSlot); // note: swapChainD->currentFrameSlot, not sc's + if (swapChainD->frameLatencyWaitableObject) + WaitForSingleObjectEx(swapChainD->frameLatencyWaitableObject, 1000, true); + HRESULT hr = cmdAllocators[currentFrameSlot]->Reset(); if (FAILED(hr)) { qWarning("Failed to reset command allocator: %s", @@ -6122,6 +6130,11 @@ void QD3D12SwapChain::destroy() dcompTarget = nullptr; } + if (frameLatencyWaitableObject) { + CloseHandle(frameLatencyWaitableObject); + frameLatencyWaitableObject = nullptr; + } + QRHI_RES_RHI(QRhiD3D12); if (rhiD) { rhiD->swapchains.remove(this); @@ -6338,6 +6351,14 @@ bool QD3D12SwapChain::createOrResize() if (swapInterval == 0 && rhiD->supportsAllowTearing) swapChainFlags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; + // maxFrameLatency 0 means no waitable object usage. + // Ignore it also when NoVSync is on, and when using WARP. + const bool useFrameLatencyWaitableObject = rhiD->maxFrameLatency != 0 + && swapInterval != 0 + && rhiD->driverInfoStruct.deviceType != QRhiDriverInfo::CpuDevice; + if (useFrameLatencyWaitableObject) + swapChainFlags |= DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; + if (!swapChain) { chooseFormats(); @@ -6394,6 +6415,10 @@ bool QD3D12SwapChain::createOrResize() qPrintable(QSystemError::windowsComString(hr))); } } + if (useFrameLatencyWaitableObject) { + swapChain->SetMaximumFrameLatency(rhiD->maxFrameLatency); + frameLatencyWaitableObject = swapChain->GetFrameLatencyWaitableObject(); + } if (dcompVisual) { hr = dcompVisual->SetContent(swapChain); if (SUCCEEDED(hr)) { diff --git a/src/gui/rhi/qrhid3d12_p.h b/src/gui/rhi/qrhid3d12_p.h index 3f9abbb5acb..72c6c95faaf 100644 --- a/src/gui/rhi/qrhid3d12_p.h +++ b/src/gui/rhi/qrhid3d12_p.h @@ -1044,6 +1044,7 @@ struct QD3D12SwapChain : public QRhiSwapChain QD3D12SwapChainRenderTarget rtWrapper; QD3D12SwapChainRenderTarget rtWrapperRight; QD3D12CommandBuffer cbWrapper; + HANDLE frameLatencyWaitableObject = nullptr; struct FrameResources { ID3D12Fence *fence = nullptr; @@ -1194,6 +1195,7 @@ public: void bindShaderVisibleHeaps(QD3D12CommandBuffer *cbD); bool debugLayer = false; + UINT maxFrameLatency = 2; // 1-3, use 2 to keep CPU-GPU parallelism while reducing lag compared to tripple buffering ID3D12Device2 *dev = nullptr; D3D_FEATURE_LEVEL minimumFeatureLevel = D3D_FEATURE_LEVEL(0); LUID adapterLuid = {};