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 = {};