diff options
author | Laszlo Agocs <laszlo.agocs@qt.io> | 2024-02-26 17:29:11 +0100 |
---|---|---|
committer | Laszlo Agocs <laszlo.agocs@qt.io> | 2024-02-29 12:08:08 +0100 |
commit | 96299182fbc0b11a89ef153bce5be8268b9d87dd (patch) | |
tree | 7e05fde745cd505d34b4aba997cbca45cd3a578e /src/quick/scenegraph/qsgthreadedrenderloop.cpp | |
parent | d7575ae2a750173786d0d98b7b9b3cc9893ac15c (diff) |
Fall back to retrying with "software" when swapchain fails
The idea being that a swapchain creation failure would end up behaving
the same as as the QRhi::create() failure, in particular on Windows:
for the latter we automatically retry with PreferSwRenderer set (i.e.
requesting WARP), but until now this has not been done upon swapchain
creation failures. (it is not clear under what circumstances we fail
to create a swapchain, but reports from Qt-based applications indicate
that this is happening out in the wild)
If the first swapchain creation attempt fails, do what a device loss
would do: drop everything, incl. the QRhi, and post a special event
to the main thread do request a new round of rendering, upon which
we'll attempt to reinitialize a QRhi and everything else in the
scenegraph. We then recognize the flag and pass it on to QSGRhiSupport
to indicate we want a software-backed adapter / physical device,
if there is one. We won't retry afterwards if that fails too.
This may also correct issues with device loss handling which did not
correctly wake up the main thread.
[ChangeLog][Qt Quick] The fallback to a software rasterizer, if
applicable to the platform and 3D API, is now performed also upon
the first swapchain creation failure. Previously this was only done
if the QRhi initialization failed. Relevant in particular on Windows,
potentially allowing functioning on systems that are incapable of
proper accelerated D3D rendering, but, for whatever reason, do not
fail early on upon the device/context creation, only later at
swapchain creation.
Pick-to: 6.7 6.6 6.5
Fixes: QTBUG-109708
Fixes: QTBUG-101200
Change-Id: I76f4aa132361d8f97ec6edfb3bf7806b4ce015b1
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Andy Nichols <andy.nichols@qt.io>
Diffstat (limited to 'src/quick/scenegraph/qsgthreadedrenderloop.cpp')
-rw-r--r-- | src/quick/scenegraph/qsgthreadedrenderloop.cpp | 50 |
1 files changed, 39 insertions, 11 deletions
diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp index 45a62022c4..a405099534 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp +++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp @@ -280,6 +280,7 @@ public: }; void ensureRhi(); + void teardownGraphics(); void handleDeviceLoss(); QSGThreadedRenderLoop *wm; @@ -309,6 +310,7 @@ public: bool rhiDeviceLost = false; bool rhiDoomed = false; bool guiNotifiedAboutRhiFailure = false; + bool swRastFallbackDueToSwapchainFailure = false; // Local event queue stuff... bool stopEventProcessing; @@ -575,22 +577,27 @@ void QSGRenderThread::sync(bool inExpose) } } -void QSGRenderThread::handleDeviceLoss() +void QSGRenderThread::teardownGraphics() { - if (!rhi || !rhi->isDeviceLost()) - return; - - qWarning("Graphics device lost, cleaning up scenegraph and releasing RHI"); QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window); wd->cleanupNodesOnShutdown(); sgrc->invalidate(); wm->releaseSwapchain(window); - rhiDeviceLost = true; if (ownRhi) QSGRhiSupport::instance()->destroyRhi(rhi, {}); rhi = nullptr; } +void QSGRenderThread::handleDeviceLoss() +{ + if (!rhi || !rhi->isDeviceLost()) + return; + + qWarning("Graphics device lost, cleaning up scenegraph and releasing RHI"); + teardownGraphics(); + rhiDeviceLost = true; +} + void QSGRenderThread::syncAndRender() { const bool profileFrames = QSG_LOG_TIME_RENDERLOOP().isDebugEnabled(); @@ -620,6 +627,7 @@ void QSGRenderThread::syncAndRender() pendingUpdate = 0; QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); + QSGRhiSupport *rhiSupport = QSGRhiSupport::instance(); // Begin the frame before syncing -> sync is where we may invoke // updatePaintNode() on the items and they may want to do resource updates. // Also relevant for applications that connect to the before/afterSynchronizing @@ -641,10 +649,29 @@ void QSGRenderThread::syncAndRender() qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "just became exposed"); cd->hasActiveSwapchain = cd->swapchain->createOrResize(); - if (!cd->hasActiveSwapchain && rhi->isDeviceLost()) { - handleDeviceLoss(); - QCoreApplication::postEvent(window, new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest))); - return; + if (!cd->hasActiveSwapchain) { + bool bailOut = false; + if (rhi->isDeviceLost()) { + handleDeviceLoss(); + bailOut = true; + } else if (previousOutputSize.isEmpty() && !swRastFallbackDueToSwapchainFailure && rhiSupport->attemptReinitWithSwRastUponFail()) { + qWarning("Failed to create swapchain." + " Retrying by requesting a software rasterizer, if applicable for the 3D API implementation."); + swRastFallbackDueToSwapchainFailure = true; + teardownGraphics(); + bailOut = true; + } + if (bailOut) { + QCoreApplication::postEvent(window, new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest))); + if (syncRequested) { + // Lock like sync() would do. Note that exposeRequested always includes syncRequested. + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- bailing out due to failed swapchain init, wake Gui"); + mutex.lock(); + waitCondition.wakeOne(); + mutex.unlock(); + } + return; + } } cd->swapchainJustBecameRenderable = false; @@ -851,7 +878,8 @@ void QSGRenderThread::ensureRhi() if (rhiDoomed) // no repeated attempts if the initial attempt failed return; QSGRhiSupport *rhiSupport = QSGRhiSupport::instance(); - QSGRhiSupport::RhiCreateResult rhiResult = rhiSupport->createRhi(window, offscreenSurface); + const bool forcePreferSwRenderer = swRastFallbackDueToSwapchainFailure; + QSGRhiSupport::RhiCreateResult rhiResult = rhiSupport->createRhi(window, offscreenSurface, forcePreferSwRenderer); rhi = rhiResult.rhi; ownRhi = rhiResult.own; if (rhi) { |