aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/scenegraph/qsgthreadedrenderloop.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/scenegraph/qsgthreadedrenderloop.cpp')
-rw-r--r--src/quick/scenegraph/qsgthreadedrenderloop.cpp123
1 files changed, 92 insertions, 31 deletions
diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
index dc1f97de54..9b288029b4 100644
--- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp
+++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
@@ -70,6 +70,8 @@
#include <private/qsgrhishadereffectnode_p.h>
#include <private/qsgdefaultrendercontext_p.h>
+#include <qtquick_tracepoints_p.h>
+
/*
Overall design:
@@ -368,6 +370,9 @@ public:
QSize windowSize;
float dpr = 1;
int rhiSampleCount = 1;
+ bool rhiDeviceLost = false;
+ bool rhiDoomed = false;
+ bool guiNotifiedAboutRhiFailure = false;
// Local event queue stuff...
bool stopEventProcessing;
@@ -622,13 +627,15 @@ void QSGRenderThread::sync(bool inExpose, bool inGrab)
sgrc->initialize(&rcParams);
}
}
- } else {
+ } else if (rhi) {
// With the rhi making the (OpenGL) context current serves only one
// purpose: to enable external OpenGL rendering connected to one of
// the QQuickWindow signals (beforeSynchronizing, beforeRendering,
// etc.) to function like it did on the direct OpenGL path. For our
// own rendering this call would not be necessary.
rhi->makeThreadLocalNativeContextCurrent();
+ } else {
+ current = false;
}
if (current) {
QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
@@ -653,6 +660,11 @@ void QSGRenderThread::sync(bool inExpose, bool inGrab)
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- window has bad size, sync aborted");
}
+ // Two special cases: For grabs we do not care about blocking the gui
+ // (main) thread. When this is from an expose, we will keep locked until
+ // the frame is rendered (submitted), so in that case waking happens later
+ // in syncAndRender(). Otherwise, wake now and let the main thread go on
+ // while we render.
if (!inExpose && !inGrab) {
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- sync complete, waking Gui");
waitCondition.wakeOne();
@@ -669,6 +681,7 @@ void QSGRenderThread::handleDeviceLoss()
QQuickWindowPrivate::get(window)->cleanupNodesOnShutdown();
sgrc->invalidate();
wm->releaseSwapchain(window);
+ rhiDeviceLost = true;
delete rhi;
rhi = nullptr;
}
@@ -680,7 +693,9 @@ void QSGRenderThread::syncAndRender(QImage *grabImage)
sinceLastTime = threadTimer.nsecsElapsed();
threadTimer.start();
}
+ Q_TRACE_SCOPE(QSG_syncAndRender);
Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphRenderLoopFrame);
+ Q_TRACE(QSG_sync_entry);
QElapsedTimer waitTimer;
waitTimer.start();
@@ -693,8 +708,9 @@ void QSGRenderThread::syncAndRender(QImage *grabImage)
const bool repaintRequested = (pendingUpdate & RepaintRequest) || d->customRenderStage || grabImage;
const bool syncRequested = (pendingUpdate & SyncRequest) || grabImage;
const bool exposeRequested = (pendingUpdate & ExposeRequest) == ExposeRequest;
- pendingUpdate = 0;
const bool grabRequested = grabImage != nullptr;
+ if (!grabRequested)
+ pendingUpdate = 0;
QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
// Begin the frame before syncing -> sync is where we may invoke
@@ -745,13 +761,11 @@ void QSGRenderThread::syncAndRender(QImage *grabImage)
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) {
+ if (syncRequested && !grabRequested) {
+ // Lock like sync() would do. Note that exposeRequested always includes syncRequested.
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- bailing out due to failed beginFrame, wake Gui");
- waitCondition.wakeOne();
- mutex.unlock();
- } else if (syncRequested && !grabRequested) {
- qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- bailing out due to failed beginFrame, wake Gui like sync would do");
mutex.lock();
+ // Go ahead with waking because we will return right after this.
waitCondition.wakeOne();
mutex.unlock();
}
@@ -767,33 +781,46 @@ void QSGRenderThread::syncAndRender(QImage *grabImage)
if (profileFrames)
syncTime = threadTimer.nsecsElapsed();
#endif
+ Q_TRACE(QSG_sync_exit);
Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
QQuickProfiler::SceneGraphRenderLoopSync);
- if (!syncResultedInChanges && !repaintRequested && sgrc->isValid() && !grabImage) {
- if (gl || !rhi->isRecordingFrame()) {
- qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- no changes, render aborted");
- int waitTime = vsyncDelta - (int) waitTimer.elapsed();
- if (waitTime > 0)
- msleep(waitTime);
- return;
- }
+ if (!syncResultedInChanges
+ && !repaintRequested
+ && !(pendingUpdate & RepaintRequest) // may have been set in sync()
+ && sgrc->isValid()
+ && !grabRequested
+ && (gl || (rhi && !rhi->isRecordingFrame())))
+ {
+ qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- no changes, render aborted");
+ int waitTime = vsyncDelta - (int) waitTimer.elapsed();
+ if (waitTime > 0)
+ msleep(waitTime);
+ return;
}
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- rendering started");
+ Q_TRACE(QSG_render_entry);
- if (animatorDriver->isRunning() && !grabImage) {
+ // RepaintRequest may have been set in pendingUpdate in an
+ // updatePaintNode() invoked from sync(). We are about to do a repaint
+ // right now, so reset the flag. (bits other than RepaintRequest cannot
+ // be set in pendingUpdate at this point)
+ if (!grabRequested)
+ pendingUpdate = 0;
+
+ if (animatorDriver->isRunning() && !grabRequested) {
d->animationController->lock();
animatorDriver->advance();
d->animationController->unlock();
}
bool current = true;
- if (d->renderer && windowSize.width() > 0 && windowSize.height() > 0) {
+ if (d->renderer && windowSize.width() > 0 && windowSize.height() > 0 && (gl || rhi)) {
if (gl)
current = gl->makeCurrent(window);
- else if (rhi)
+ else
rhi->makeThreadLocalNativeContextCurrent();
} else {
current = false;
@@ -811,21 +838,23 @@ void QSGRenderThread::syncAndRender(QImage *grabImage)
if (profileFrames)
renderTime = threadTimer.nsecsElapsed();
+ Q_TRACE(QSG_render_exit);
Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
QQuickProfiler::SceneGraphRenderLoopRender);
+ Q_TRACE(QSG_swap_entry);
// With the rhi grabs can only be done by adding a readback and then
// blocking in a real frame. The legacy GL path never gets here with
// grabs as it rather invokes sync/render directly without going
// through syncAndRender().
- if (grabImage) {
+ if (grabRequested) {
Q_ASSERT(rhi && !gl && cd->swapchain);
*grabImage = QSGRhiSupport::instance()->grabAndBlockInCurrentFrame(rhi, cd->swapchain);
}
if (cd->swapchain) {
- QRhi::EndFrameFlags flags = 0;
- if (grabImage)
+ QRhi::EndFrameFlags flags;
+ if (grabRequested)
flags |= QRhi::SkipPresent;
QRhi::FrameOpResult frameResult = rhi->endFrame(cd->swapchain, flags);
if (frameResult != QRhi::FrameOpSuccess) {
@@ -841,12 +870,13 @@ void QSGRenderThread::syncAndRender(QImage *grabImage)
gl->swapBuffers(window);
}
- if (!grabImage)
+ if (!grabRequested)
d->fireFrameSwapped();
-
} else {
+ Q_TRACE(QSG_render_exit);
Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphRenderLoopFrame,
QQuickProfiler::SceneGraphRenderLoopSync, 1);
+ Q_TRACE(QSG_swap_entry);
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- window not ready, skipping render");
}
@@ -858,7 +888,8 @@ void QSGRenderThread::syncAndRender(QImage *grabImage)
// has started rendering with a bad window, causing makeCurrent to
// fail or if the window has a bad size.
if (exposeRequested) {
- qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- wake Gui after initial expose");
+ // With expose sync() did not wake gui, do it now.
+ qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- wake Gui after expose");
waitCondition.wakeOne();
mutex.unlock();
}
@@ -870,7 +901,7 @@ void QSGRenderThread::syncAndRender(QImage *grabImage)
int((renderTime - syncTime) / 1000000),
int(threadTimer.elapsed() - renderTime / 1000000));
-
+ Q_TRACE(QSG_swap_exit);
Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame,
QQuickProfiler::SceneGraphRenderLoopSwap);
@@ -912,14 +943,21 @@ void QSGRenderThread::processEventsAndWaitForMore()
void QSGRenderThread::ensureRhi()
{
if (!rhi) {
+ if (rhiDoomed) // no repeated attempts if the initial attempt failed
+ return;
QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
rhi = rhiSupport->createRhi(window, offscreenSurface);
if (rhi) {
+ rhiDeviceLost = false;
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 (!rhiDeviceLost) {
+ rhiDoomed = true;
+ qWarning("Failed to create QRhi on the render thread; scenegraph is not functional");
+ }
+ // otherwise no error, will retry on a subsequent rendering attempt
return;
}
}
@@ -975,9 +1013,24 @@ void QSGRenderThread::run()
if (window) {
if (enableRhi) {
+
ensureRhi();
- if (rhi)
- syncAndRender();
+
+ // We absolutely have to syncAndRender() here, even when QRhi
+ // failed to initialize otherwise the gui thread will be left
+ // in a blocked state. It is up to syncAndRender() to
+ // gracefully skip all graphics stuff when rhi is null.
+
+ syncAndRender();
+
+ // Now we can do something about rhi init failures. (reinit
+ // failure after device reset does not count)
+ if (rhiDoomed && !guiNotifiedAboutRhiFailure) {
+ guiNotifiedAboutRhiFailure = true;
+ QEvent *e = new QEvent(QEvent::Type(QQuickWindowPrivate::TriggerContextCreationFailure));
+ QCoreApplication::postEvent(window, e);
+ }
+
} else {
if (!sgrc->openglContext() && windowSize.width() > 0 && windowSize.height() > 0 && gl->makeCurrent(window)) {
QSGDefaultRenderContext::InitParams rcParams;
@@ -1282,10 +1335,9 @@ void QSGThreadedRenderLoop::handleExposure(QQuickWindow *window)
w->thread->gl->setFormat(w->window->requestedFormat());
w->thread->gl->setScreen(w->window->screen());
if (!w->thread->gl->create()) {
- const bool isEs = w->thread->gl->isOpenGLES();
delete w->thread->gl;
w->thread->gl = nullptr;
- handleContextCreationFailure(w->window, isEs);
+ handleContextCreationFailure(w->window);
return;
}
@@ -1508,7 +1560,7 @@ void QSGThreadedRenderLoop::polishAndSync(Window *w, bool inExpose)
return;
}
-
+ Q_TRACE_SCOPE(QSG_polishAndSync);
QElapsedTimer timer;
qint64 polishTime = 0;
qint64 waitTime = 0;
@@ -1517,14 +1569,17 @@ void QSGThreadedRenderLoop::polishAndSync(Window *w, bool inExpose)
if (profileFrames)
timer.start();
Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishAndSync);
+ Q_TRACE(QSG_polishItems_entry);
QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
d->polishItems();
if (profileFrames)
polishTime = timer.nsecsElapsed();
+ Q_TRACE(QSG_polishItems_exit);
Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync,
QQuickProfiler::SceneGraphPolishAndSyncPolish);
+ Q_TRACE(QSG_wait_entry);
w->updateDuringSync = false;
@@ -1539,8 +1594,11 @@ void QSGThreadedRenderLoop::polishAndSync(Window *w, bool inExpose)
qCDebug(QSG_LOG_RENDERLOOP, "- wait for sync");
if (profileFrames)
waitTime = timer.nsecsElapsed();
+ Q_TRACE(QSG_wait_exit);
Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync,
QQuickProfiler::SceneGraphPolishAndSyncWait);
+ Q_TRACE(QSG_sync_entry);
+
w->thread->waitCondition.wait(&w->thread->mutex);
m_lockedForSync = false;
w->thread->mutex.unlock();
@@ -1548,8 +1606,10 @@ void QSGThreadedRenderLoop::polishAndSync(Window *w, bool inExpose)
if (profileFrames)
syncTime = timer.nsecsElapsed();
+ Q_TRACE(QSG_sync_exit);
Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync,
QQuickProfiler::SceneGraphPolishAndSyncSync);
+ Q_TRACE(QSG_animations_entry);
if (m_animation_timer == 0 && m_animation_driver->isRunning()) {
qCDebug(QSG_LOG_RENDERLOOP, "- advancing animations");
@@ -1570,6 +1630,7 @@ void QSGThreadedRenderLoop::polishAndSync(Window *w, bool inExpose)
<< ", animations=" << (timer.nsecsElapsed() - syncTime) / 1000000
<< " - (on Gui thread) " << window;
+ Q_TRACE(QSG_animations_exit);
Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphPolishAndSync,
QQuickProfiler::SceneGraphPolishAndSyncAnimations);
}