rhi: d3d11: Wait in beginFrame with a max latency of 2
Implements what's described in https://learn.microsoft.com/en-us/windows/uwp/gaming/reduce-latency-with-dxgi-1-3-swap-chains By default SetMaximumFrameLatency is called with a value of 2, since we do not want to force 1 on everyone as some application may have more GPU heavy 2D/3D scenes. Use QT_D3D_MAX_FRAME_LATENCY to override. Setting to 0 disables the whole thing. This patch contains no C++ API (could be in the backend-specific init params), because it is not clear if it is reasonable to expect applications to control such details of the presentation mechanism. This also allows cherry picking to earlier branches. The result is that, for example, the visible lag between the mouse cursor and the dragged item will get reduced in Qt Quick scenes that have a mouse draggable item in them. This comes at the expense of potentially reducing CPU-GPU parallelism, but many scenes do not need that kind of performance to begin with. One big gain here is that with a MaximumFrameLatency of 2 the behavior becomes closer, and basically identical, to what one gets when running with OpenGL (where Qt has no control whatsoever over such presentation details), and so the behavior becomes closer out of the box with Qt 6 to what one got with Qt 5. This is true at least with NVIDIA graphics on Windows 11; note that it may not apply to other vendors' GL implementations. The QRhi doc update just brings in what's been true for some time in Qt 6: as IDXGIFactory2 is required, the minimum required DXGI version is in fact 1.3 and so D3D 11.2. (which is Windows 8.1 stuff so should be fine) [ChangeLog][RHI] The D3D11 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: I74f68f7af41097b957b8e0bbdbae39f9302b1ad3 Reviewed-by: Andy Nichols <andy.nichols@qt.io> (cherry picked from commit 02bf4d06b2432d95df9a1f6c9ad072b953b06cda) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
parent
0a67a8c3d4
commit
9200606068
@ -72,10 +72,10 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
|
||||
builds on QOpenGLContext, QOpenGLFunctions, and the related cross-platform
|
||||
infrastructure of the Qt GUI module.
|
||||
|
||||
\li Direct3D 11.1 or newer, with Shader Model 5.0 or newer. When the D3D
|
||||
runtime has no support for 11.1 features or Shader Model 5.0,
|
||||
initialization using an accelerated graphics device will fail, but using
|
||||
the
|
||||
\li Direct3D 11.2 and newer (with DXGI 1.3 and newer), using Shader Model
|
||||
5.0 or newer. When the D3D runtime has no support for 11.2 features or
|
||||
Shader Model 5.0, initialization using an accelerated graphics device will
|
||||
fail, but using the
|
||||
\l{https://learn.microsoft.com/en-us/windows/win32/direct3darticles/directx-warp}{software
|
||||
adapter} is still an option.
|
||||
|
||||
|
@ -222,9 +222,19 @@ bool QRhiD3D11::create(QRhi::Flags flags)
|
||||
// there. (some features are not supported then, however)
|
||||
useLegacySwapchainModel = qEnvironmentVariableIntValue("QT_D3D_NO_FLIP");
|
||||
|
||||
qCDebug(QRHI_LOG_INFO, "FLIP_* swapchain supported = true, ALLOW_TEARING supported = %s, use legacy (non-FLIP) model = %s",
|
||||
if (!useLegacySwapchainModel) {
|
||||
if (qEnvironmentVariableIsSet("QT_D3D_MAX_FRAME_LATENCY"))
|
||||
maxFrameLatency = UINT(qMax(0, qEnvironmentVariableIntValue("QT_D3D_MAX_FRAME_LATENCY")));
|
||||
} else {
|
||||
maxFrameLatency = 0;
|
||||
}
|
||||
|
||||
qCDebug(QRHI_LOG_INFO, "FLIP_* swapchain supported = true, ALLOW_TEARING supported = %s, use legacy (non-FLIP) model = %s, max frame latency = %u",
|
||||
supportsAllowTearing ? "true" : "false",
|
||||
useLegacySwapchainModel ? "true" : "false");
|
||||
useLegacySwapchainModel ? "true" : "false",
|
||||
maxFrameLatency);
|
||||
if (maxFrameLatency == 0)
|
||||
qCDebug(QRHI_LOG_INFO, "Disabling FRAME_LATENCY_WAITABLE_OBJECT usage");
|
||||
|
||||
if (!importedDeviceAndContext) {
|
||||
IDXGIAdapter1 *adapter;
|
||||
@ -1340,6 +1350,10 @@ QRhi::FrameOpResult QRhiD3D11::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginF
|
||||
contextState.currentSwapChain = swapChainD;
|
||||
const int currentFrameSlot = swapChainD->currentFrameSlot;
|
||||
|
||||
// if we have a waitable object, now is the time to wait on it
|
||||
if (swapChainD->frameLatencyWaitableObject)
|
||||
WaitForSingleObjectEx(swapChainD->frameLatencyWaitableObject, 1000, true);
|
||||
|
||||
swapChainD->cb.resetState();
|
||||
|
||||
swapChainD->rt.d.rtv[0] = swapChainD->sampleDesc.Count > 1 ?
|
||||
@ -4949,6 +4963,11 @@ void QD3D11SwapChain::destroy()
|
||||
dcompTarget = nullptr;
|
||||
}
|
||||
|
||||
if (frameLatencyWaitableObject) {
|
||||
CloseHandle(frameLatencyWaitableObject);
|
||||
frameLatencyWaitableObject = nullptr;
|
||||
}
|
||||
|
||||
QRHI_RES_RHI(QRhiD3D11);
|
||||
if (rhiD) {
|
||||
rhiD->unregisterResource(this);
|
||||
@ -5132,6 +5151,17 @@ bool QD3D11SwapChain::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) {
|
||||
// the flag is not supported in real fullscreen on D3D11, but perhaps that's fine since we only do borderless
|
||||
swapChainFlags |= DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
|
||||
}
|
||||
|
||||
if (!swapChain) {
|
||||
sampleDesc = rhiD->effectiveSampleDesc(m_sampleCount);
|
||||
colorFormat = DEFAULT_FORMAT;
|
||||
@ -5217,16 +5247,31 @@ bool QD3D11SwapChain::createOrResize()
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
swapChain = sc1;
|
||||
if (m_format != SDR) {
|
||||
IDXGISwapChain3 *sc3 = nullptr;
|
||||
if (SUCCEEDED(sc1->QueryInterface(__uuidof(IDXGISwapChain3), reinterpret_cast<void **>(&sc3)))) {
|
||||
IDXGISwapChain3 *sc3 = nullptr;
|
||||
if (SUCCEEDED(sc1->QueryInterface(__uuidof(IDXGISwapChain3), reinterpret_cast<void **>(&sc3)))) {
|
||||
if (m_format != SDR) {
|
||||
hr = sc3->SetColorSpace1(hdrColorSpace);
|
||||
if (FAILED(hr))
|
||||
qWarning("Failed to set color space on swapchain: %s",
|
||||
qPrintable(QSystemError::windowsComString(hr)));
|
||||
sc3->Release();
|
||||
} else {
|
||||
qPrintable(QSystemError::windowsComString(hr)));
|
||||
}
|
||||
if (useFrameLatencyWaitableObject) {
|
||||
sc3->SetMaximumFrameLatency(rhiD->maxFrameLatency);
|
||||
frameLatencyWaitableObject = sc3->GetFrameLatencyWaitableObject();
|
||||
}
|
||||
sc3->Release();
|
||||
} else {
|
||||
if (m_format != SDR)
|
||||
qWarning("IDXGISwapChain3 not available, HDR swapchain will not work as expected");
|
||||
if (useFrameLatencyWaitableObject) {
|
||||
IDXGISwapChain2 *sc2 = nullptr;
|
||||
if (SUCCEEDED(sc1->QueryInterface(__uuidof(IDXGISwapChain2), reinterpret_cast<void **>(&sc2)))) {
|
||||
sc2->SetMaximumFrameLatency(rhiD->maxFrameLatency);
|
||||
frameLatencyWaitableObject = sc2->GetFrameLatencyWaitableObject();
|
||||
sc2->Release();
|
||||
} else { // this cannot really happen since we require DXGIFactory2
|
||||
qWarning("IDXGISwapChain2 not available, FrameLatencyWaitableObject cannot be used");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dcompVisual) {
|
||||
|
@ -625,6 +625,7 @@ struct QD3D11SwapChain : public QRhiSwapChain
|
||||
IDCompositionVisual *dcompVisual = nullptr;
|
||||
QD3D11SwapChainTimestamps timestamps;
|
||||
int currentTimestampPairIndex = 0;
|
||||
HANDLE frameLatencyWaitableObject = nullptr;
|
||||
};
|
||||
|
||||
class QRhiD3D11 : public QRhiImplementation
|
||||
@ -760,6 +761,7 @@ public:
|
||||
|
||||
QRhi::Flags rhiFlags;
|
||||
bool debugLayer = false;
|
||||
UINT maxFrameLatency = 2; // 1-3, use 2 to keep CPU-GPU parallelism while reducing lag compared to tripple buffering
|
||||
bool importedDeviceAndContext = false;
|
||||
ID3D11Device *dev = nullptr;
|
||||
ID3D11DeviceContext1 *context = nullptr;
|
||||
|
Loading…
x
Reference in New Issue
Block a user