rhi: d3d: Fix up non-vsynced presentation

Doing Present(0, 0) is not necessarily sufficient to get rid of
blocking. It may very well start blocking after a few frames.

This does not apply to a non-flip-discard swapchain (when running
with QT_D3D_NO_FLIP=1), but for flip-discard we should also try using
DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING and DXGI_PRESENT_ALLOW_TEARING in
case a swap interval of 0 is wanted.

Pick-to: 6.3
Fixes: QTBUG-99949
Change-Id: I9cb13b139ba04e41b4f25b94bcd3d1e973496414
Reviewed-by: Andy Nichols <andy.nichols@qt.io>
This commit is contained in:
Laszlo Agocs 2022-01-17 17:27:17 +01:00
parent d671bab0e3
commit 6dd09664b2
2 changed files with 48 additions and 24 deletions

View File

@ -202,19 +202,32 @@ bool QRhiD3D11::create(QRhi::Flags flags)
dxgiFactory = createDXGIFactory2();
if (dxgiFactory != nullptr) {
hasDxgi2 = true;
supportsFlipDiscardSwapchain = !qEnvironmentVariableIntValue("QT_D3D_NO_FLIP");
} else {
dxgiFactory = createDXGIFactory1();
hasDxgi2 = false;
supportsFlipDiscardSwapchain = false;
}
if (dxgiFactory == nullptr)
return false;
qCDebug(QRHI_LOG_INFO, "DXGI 1.2 = %s, FLIP_DISCARD swapchain supported = %s",
hasDxgi2 ? "true" : "false", supportsFlipDiscardSwapchain ? "true" : "false");
supportsAllowTearing = false;
if (supportsFlipDiscardSwapchain) {
// For a FLIP_DISCARD swapchain Present(0, 0) is not necessarily
// sufficient to get non-blocking behavior, try using ALLOW_TEARING
// when available.
IDXGIFactory5 *factory5 = nullptr;
if (SUCCEEDED(dxgiFactory->QueryInterface(IID_IDXGIFactory5, reinterpret_cast<void **>(&factory5)))) {
BOOL allowTearing = false;
if (SUCCEEDED(factory5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allowTearing, sizeof(allowTearing))))
supportsAllowTearing = allowTearing;
factory5->Release();
}
}
qCDebug(QRHI_LOG_INFO, "FLIP_DISCARD swapchain supported = %s, ALLOW_TEARING supported = %s",
supportsFlipDiscardSwapchain ? "true" : "false",
supportsAllowTearing ? "true" : "false");
if (!importedDeviceAndContext) {
IDXGIAdapter1 *adapter;
@ -1160,7 +1173,9 @@ QRhi::FrameOpResult QRhiD3D11::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame
}
if (!flags.testFlag(QRhi::SkipPresent)) {
const UINT presentFlags = 0;
UINT presentFlags = 0;
if (swapChainD->swapInterval == 0 && (swapChainD->swapChainFlags & DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING))
presentFlags |= DXGI_PRESENT_ALLOW_TEARING;
HRESULT hr = swapChainD->swapChain->Present(swapChainD->swapInterval, presentFlags);
if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) {
qWarning("Device loss detected in Present()");
@ -4551,27 +4566,36 @@ bool QD3D11SwapChain::createOrResize()
return false;
QRHI_RES_RHI(QRhiD3D11);
bool useFlipDiscard = rhiD->hasDxgi2 && rhiD->supportsFlipDiscardSwapchain;
bool useFlipDiscard = rhiD->supportsFlipDiscardSwapchain;
// Take a shortcut for alpha: whatever the platform plugin does to enable
// transparency for our QWindow will be sufficient on the legacy (DISCARD)
// path. For FLIP_DISCARD we'd need to use DirectComposition (create a
// IDCompositionDevice/Target/Visual), avoid that for now. (this though
// means HDR and semi-transparent windows cannot be combined)
if (m_flags.testFlag(SurfaceHasPreMulAlpha) || m_flags.testFlag(SurfaceHasNonPreMulAlpha)) {
useFlipDiscard = false;
if (window->requestedFormat().alphaBufferSize() <= 0)
qWarning("Swapchain says surface has alpha but the window has no alphaBufferSize set. "
"This may lead to problems.");
}
swapInterval = m_flags.testFlag(QRhiSwapChain::NoVSync) ? 0 : 1;
swapChainFlags = 0;
// A non-flip swapchain can do Present(0) as expected without
// ALLOW_TEARING, and ALLOW_TEARING is not compatible with it at all so the
// flag must not be set then. Whereas for flip-discard we should use it, if
// supported, to get better results for 'unthrottled' presentation.
if (swapInterval == 0 && useFlipDiscard && rhiD->supportsAllowTearing)
swapChainFlags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
const UINT swapChainFlags = 0;
if (!swapChain) {
HWND hwnd = reinterpret_cast<HWND>(window->winId());
sampleDesc = rhiD->effectiveSampleCount(m_sampleCount);
colorFormat = DEFAULT_FORMAT;
srgbAdjustedColorFormat = m_flags.testFlag(sRGB) ? DEFAULT_SRGB_FORMAT : DEFAULT_FORMAT;
// Take a shortcut for alpha: our QWindow is OpenGLSurface so whatever
// the platform plugin does to enable transparency for OpenGL window
// will be sufficient for us too on the legacy (DISCARD) path. For
// FLIP_DISCARD we'd need to use DirectComposition (create a
// IDCompositionDevice/Target/Visual), avoid that for now.
if (m_flags.testFlag(SurfaceHasPreMulAlpha) || m_flags.testFlag(SurfaceHasNonPreMulAlpha)) {
useFlipDiscard = false;
if (window->requestedFormat().alphaBufferSize() <= 0)
qWarning("Swapchain says surface has alpha but the window has no alphaBufferSize set. "
"This may lead to problems.");
}
HRESULT hr;
if (useFlipDiscard) {
DXGI_COLOR_SPACE_TYPE hdrColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; // SDR
@ -4651,9 +4675,9 @@ bool QD3D11SwapChain::createOrResize()
}
}
} else {
// Windows 7 for instance. Use DISCARD mode. Regardless, keep on
// using our manual resolve for symmetry with the FLIP_DISCARD code
// path when MSAA is requested.
// Fallback: use DISCARD mode. Regardless, keep on using our manual
// resolve for symmetry with the FLIP_DISCARD code path when MSAA
// is requested. This has no HDR support.
DXGI_SWAP_CHAIN_DESC desc;
memset(&desc, 0, sizeof(desc));
@ -4749,7 +4773,6 @@ bool QD3D11SwapChain::createOrResize()
currentFrameSlot = 0;
frameCount = 0;
ds = m_depthStencil ? QRHI_RES(QD3D11RenderBuffer, m_depthStencil) : nullptr;
swapInterval = m_flags.testFlag(QRhiSwapChain::NoVSync) ? 0 : 1;
QD3D11ReferenceRenderTarget *rtD = QRHI_RES(QD3D11ReferenceRenderTarget, &rt);
rtD->d.rp = QRHI_RES(QD3D11RenderPassDescriptor, m_renderPassDesc);

View File

@ -576,6 +576,7 @@ struct QD3D11SwapChain : public QRhiSwapChain
DXGI_FORMAT colorFormat;
DXGI_FORMAT srgbAdjustedColorFormat;
IDXGISwapChain *swapChain = nullptr;
UINT swapChainFlags = 0;
static const int BUFFER_COUNT = 2;
ID3D11Texture2D *backBufferTex;
ID3D11RenderTargetView *backBufferRtv;
@ -727,8 +728,8 @@ public:
ID3DUserDefinedAnnotation *annotations = nullptr;
IDXGIAdapter1 *activeAdapter = nullptr;
IDXGIFactory1 *dxgiFactory = nullptr;
bool hasDxgi2 = false;
bool supportsFlipDiscardSwapchain = false;
bool supportsAllowTearing = false;
bool deviceLost = false;
QRhiD3D11NativeHandles nativeHandlesStruct;
QRhiDriverInfo driverInfoStruct;