aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2019-09-10 15:50:49 +0200
committerLaszlo Agocs <laszlo.agocs@qt.io>2019-09-23 12:50:13 +0200
commit18955f3c89443d13a0b6dfa8056df1d38d5bbff6 (patch)
tree740a70935464a6b1cc15ee67ae27be17cc06f51a /src
parent5f4b7f37a430135cbbf930373e0e9682e9a3a2ac (diff)
Recover from device lost on the rhi path in the threaded loop
Change-Id: I90d43d5daa75bbc52c9c10f4ef920b898bbd39d4 Reviewed-by: Christian Strømme <christian.stromme@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/quick/scenegraph/qsgthreadedrenderloop.cpp161
1 files changed, 100 insertions, 61 deletions
diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
index eb6eec342c..cbf57225b0 100644
--- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp
+++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
@@ -199,7 +199,7 @@ public:
WMSyncEvent(QQuickWindow *c, bool inExpose, bool force)
: WMWindowEvent(c, WM_RequestSync)
, size(c->size())
- , dpr(c->effectiveDevicePixelRatio())
+ , dpr(float(c->effectiveDevicePixelRatio()))
, syncInExpose(inExpose)
, forceRenderPass(force)
{}
@@ -306,8 +306,7 @@ public:
delete offscreenSurface;
}
- void invalidateOpenGL(QQuickWindow *window, bool inDestructor, QOffscreenSurface *backupSurface);
- void initializeOpenGL();
+ void invalidateGraphics(QQuickWindow *window, bool inDestructor, QOffscreenSurface *backupSurface);
bool event(QEvent *) override;
void run() override;
@@ -340,6 +339,9 @@ public:
ExposeRequest = 0x04 | RepaintRequest | SyncRequest
};
+ void ensureRhi();
+ void handleDeviceLoss();
+
QSGThreadedRenderLoop *wm;
QOpenGLContext *gl;
bool enableRhi;
@@ -419,9 +421,9 @@ bool QSGRenderThread::event(QEvent *e)
WMTryReleaseEvent *wme = static_cast<WMTryReleaseEvent *>(e);
if (!window || wme->inDestructor) {
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- setting exit flag and invalidating OpenGL");
- invalidateOpenGL(wme->window, wme->inDestructor, wme->needsFallback ? offscreenSurface : nullptr);
+ invalidateGraphics(wme->window, wme->inDestructor, wme->needsFallback ? offscreenSurface : nullptr);
active = gl || rhi;
- Q_ASSERT_X(!wme->inDestructor || !active, "QSGRenderThread::invalidateOpenGL()", "Thread's active state is not set to false when shutting down");
+ Q_ASSERT_X(!wme->inDestructor || !active, "QSGRenderThread::invalidateGraphics()", "Thread's active state is not set to false when shutting down");
if (sleeping)
stopEventProcessing = true;
} else {
@@ -512,9 +514,9 @@ bool QSGRenderThread::event(QEvent *e)
return QThread::event(e);
}
-void QSGRenderThread::invalidateOpenGL(QQuickWindow *window, bool inDestructor, QOffscreenSurface *fallback)
+void QSGRenderThread::invalidateGraphics(QQuickWindow *window, bool inDestructor, QOffscreenSurface *fallback)
{
- qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "invalidateOpenGL()");
+ qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "invalidateGraphics()");
if (!gl && !rhi)
return;
@@ -615,7 +617,7 @@ void QSGRenderThread::sync(bool inExpose, bool inGrab)
QSGDefaultRenderContext::InitParams rcParams;
rcParams.sampleCount = qMax(1, gl->format().samples());
rcParams.openGLContext = gl;
- rcParams.initialSurfacePixelSize = windowSize * dpr;
+ rcParams.initialSurfacePixelSize = windowSize * qreal(dpr);
rcParams.maybeSurface = window;
sgrc->initialize(&rcParams);
}
@@ -658,6 +660,19 @@ void QSGRenderThread::sync(bool inExpose, bool inGrab)
}
}
+void QSGRenderThread::handleDeviceLoss()
+{
+ if (!rhi || !rhi->isDeviceLost())
+ return;
+
+ qWarning("Graphics device lost, cleaning up scenegraph and releasing RHI");
+ QQuickWindowPrivate::get(window)->cleanupNodesOnShutdown();
+ sgrc->invalidate();
+ wm->releaseSwapchain(window);
+ delete rhi;
+ rhi = nullptr;
+}
+
void QSGRenderThread::syncAndRender(QImage *grabImage)
{
bool profileFrames = QSG_LOG_TIME_RENDERLOOP().isDebugEnabled();
@@ -699,13 +714,20 @@ void QSGRenderThread::syncAndRender(QImage *grabImage)
if (previousOutputSize != effectiveOutputSize || cd->swapchainJustBecameRenderable) {
if (cd->swapchainJustBecameRenderable)
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "just became exposed");
- cd->swapchainJustBecameRenderable = false;
- cd->depthStencilForSwapchain->setPixelSize(effectiveOutputSize);
+ cd->depthStencilForSwapchain->setPixelSize(effectiveOutputSize);
cd->depthStencilForSwapchain->build();
+
cd->hasActiveSwapchain = cd->swapchain->buildOrResize();
+ if (!cd->hasActiveSwapchain && rhi->isDeviceLost()) {
+ handleDeviceLoss();
+ QCoreApplication::postEvent(window, new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)));
+ return;
+ }
+ cd->swapchainJustBecameRenderable = false;
cd->hasRenderableSwapchain = cd->hasActiveSwapchain;
+
if (!cd->hasActiveSwapchain)
qWarning("Failed to build or resize swapchain");
else
@@ -718,13 +740,12 @@ void QSGRenderThread::syncAndRender(QImage *grabImage)
QRhi::FrameOpResult frameResult = rhi->beginFrame(cd->swapchain, frameFlags);
if (frameResult != QRhi::FrameOpSuccess) {
if (frameResult == QRhi::FrameOpDeviceLost)
- qWarning("Device lost");
+ handleDeviceLoss();
else if (frameResult == QRhi::FrameOpError)
qWarning("Failed to start frame");
// try again later
if (frameResult == QRhi::FrameOpDeviceLost || frameResult == QRhi::FrameOpSwapChainOutOfDate)
QCoreApplication::postEvent(window, new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)));
-
// Before returning we need to ensure the same wake up logic that
// would have happened if beginFrame() had suceeded.
if (exposeRequested) {
@@ -810,7 +831,15 @@ void QSGRenderThread::syncAndRender(QImage *grabImage)
QRhi::EndFrameFlags flags = 0;
if (grabImage)
flags |= QRhi::SkipPresent;
- rhi->endFrame(cd->swapchain, flags);
+ QRhi::FrameOpResult frameResult = rhi->endFrame(cd->swapchain, flags);
+ if (frameResult != QRhi::FrameOpSuccess) {
+ if (frameResult == QRhi::FrameOpDeviceLost)
+ handleDeviceLoss();
+ else if (frameResult == QRhi::FrameOpError)
+ qWarning("Failed to end frame");
+ if (frameResult == QRhi::FrameOpDeviceLost || frameResult == QRhi::FrameOpSwapChainOutOfDate)
+ QCoreApplication::postEvent(window, new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)));
+ }
} else {
if (!cd->customRenderStage || !cd->customRenderStage->swap())
gl->swapBuffers(window);
@@ -884,6 +913,57 @@ void QSGRenderThread::processEventsAndWaitForMore()
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "--- done processEventsAndWaitForMore()");
}
+void QSGRenderThread::ensureRhi()
+{
+ if (!rhi) {
+ QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
+ rhi = rhiSupport->createRhi(window, offscreenSurface);
+ if (rhi) {
+ rhiSampleCount = rhiSupport->chooseSampleCountForWindowWithRhi(window, rhi);
+ if (rhiSupport->isProfilingRequested())
+ QSGRhiProfileConnection::instance()->initialize(rhi); // ### this breaks down with multiple windows
+ } else {
+ qWarning("Failed to create QRhi on the render thread; scenegraph is not functional");
+ return;
+ }
+ }
+ if (!sgrc->rhi() && windowSize.width() > 0 && windowSize.height() > 0) {
+ rhi->makeThreadLocalNativeContextCurrent();
+ QSGDefaultRenderContext::InitParams rcParams;
+ rcParams.rhi = rhi;
+ rcParams.sampleCount = rhiSampleCount;
+ rcParams.openGLContext = nullptr;
+ rcParams.initialSurfacePixelSize = windowSize * qreal(dpr);
+ rcParams.maybeSurface = window;
+ sgrc->initialize(&rcParams);
+ }
+ QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
+ if (rhi && !cd->swapchain) {
+ cd->rhi = rhi;
+ QRhiSwapChain::Flags flags = QRhiSwapChain::UsedAsTransferSource; // may be used in a grab
+ // QQ is always premul alpha. Decide based on alphaBufferSize in
+ // requestedFormat(). (the platform plugin can override format() but
+ // what matters here is what the application wanted, hence using the
+ // requested one)
+ const bool alpha = window->requestedFormat().alphaBufferSize() > 0;
+ if (alpha)
+ flags |= QRhiSwapChain::SurfaceHasPreMulAlpha;
+ cd->swapchain = rhi->newSwapChain();
+ cd->depthStencilForSwapchain = rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil,
+ QSize(),
+ rhiSampleCount,
+ QRhiRenderBuffer::UsedWithSwapChainOnly);
+ cd->swapchain->setWindow(window);
+ cd->swapchain->setDepthStencil(cd->depthStencilForSwapchain);
+ qCDebug(QSG_LOG_INFO, "MSAA sample count for the swapchain is %d. Alpha channel requested = %s.",
+ rhiSampleCount, alpha ? "yes" : "no");
+ cd->swapchain->setSampleCount(rhiSampleCount);
+ cd->swapchain->setFlags(flags);
+ cd->rpDescForSwapchain = cd->swapchain->newCompatibleRenderPassDescriptor();
+ cd->swapchain->setRenderPassDescriptor(cd->rpDescForSwapchain);
+ }
+}
+
void QSGRenderThread::run()
{
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "run()");
@@ -899,63 +979,20 @@ void QSGRenderThread::run()
if (window) {
if (enableRhi) {
- if (!rhi) {
- QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
- rhi = rhiSupport->createRhi(window, offscreenSurface);
- if (rhi) {
- rhiSampleCount = rhiSupport->chooseSampleCountForWindowWithRhi(window, rhi);
- if (rhiSupport->isProfilingRequested())
- QSGRhiProfileConnection::instance()->initialize(rhi); // ### this breaks down with multiple windows
- } else {
- qWarning("Failed to create QRhi on the render thread; scenegraph is not functional");
- }
- }
- if (!sgrc->rhi() && windowSize.width() > 0 && windowSize.height() > 0) {
- rhi->makeThreadLocalNativeContextCurrent();
- QSGDefaultRenderContext::InitParams rcParams;
- rcParams.rhi = rhi;
- rcParams.sampleCount = rhiSampleCount;
- rcParams.openGLContext = gl;
- rcParams.initialSurfacePixelSize = windowSize * dpr;
- rcParams.maybeSurface = window;
- sgrc->initialize(&rcParams);
- }
- QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
- if (rhi && !cd->swapchain) {
- cd->rhi = rhi;
- QRhiSwapChain::Flags flags = QRhiSwapChain::UsedAsTransferSource; // may be used in a grab
- // QQ is always premul alpha. Decide based on alphaBufferSize in
- // requestedFormat(). (the platform plugin can override format() but
- // what matters here is what the application wanted, hence using the
- // requested one)
- const bool alpha = window->requestedFormat().alphaBufferSize() > 0;
- if (alpha)
- flags |= QRhiSwapChain::SurfaceHasPreMulAlpha;
- cd->swapchain = rhi->newSwapChain();
- cd->depthStencilForSwapchain = rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil,
- QSize(),
- rhiSampleCount,
- QRhiRenderBuffer::UsedWithSwapChainOnly);
- cd->swapchain->setWindow(window);
- cd->swapchain->setDepthStencil(cd->depthStencilForSwapchain);
- qCDebug(QSG_LOG_INFO, "MSAA sample count for the swapchain is %d. Alpha channel requested = %s.",
- rhiSampleCount, alpha ? "yes" : "no");
- cd->swapchain->setSampleCount(rhiSampleCount);
- cd->swapchain->setFlags(flags);
- cd->rpDescForSwapchain = cd->swapchain->newCompatibleRenderPassDescriptor();
- cd->swapchain->setRenderPassDescriptor(cd->rpDescForSwapchain);
- }
+ ensureRhi();
+ if (rhi)
+ syncAndRender();
} else {
if (!sgrc->openglContext() && windowSize.width() > 0 && windowSize.height() > 0 && gl->makeCurrent(window)) {
QSGDefaultRenderContext::InitParams rcParams;
rcParams.sampleCount = qMax(1, gl->format().samples());
rcParams.openGLContext = gl;
- rcParams.initialSurfacePixelSize = windowSize * dpr;
+ rcParams.initialSurfacePixelSize = windowSize * qreal(dpr);
rcParams.maybeSurface = window;
sgrc->initialize(&rcParams);
}
+ syncAndRender();
}
- syncAndRender();
}
processEvents();
@@ -1362,6 +1399,8 @@ void QSGThreadedRenderLoop::maybeUpdate(Window *w)
return;
QThread *current = QThread::currentThread();
+ if (current == w->thread && w->thread->rhi && w->thread->rhi->isDeviceLost())
+ return;
if (current != QCoreApplication::instance()->thread() && (current != w->thread || !m_lockedForSync)) {
qWarning() << "Updates can only be scheduled from GUI thread or from QQuickItem::updatePaintNode()";
return;