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 <andy.nichols@qt.io>
(cherry picked from commit 91cae3f9cc9cb97eab69e39d405000f2fd1e533a)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Laszlo Agocs 2024-07-22 14:55:36 +02:00 committed by Qt Cherry-pick Bot
parent 9200606068
commit 1e6950fd77
2 changed files with 27 additions and 0 deletions

View File

@ -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<void **>(&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)) {

View File

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