From c5ac1b8a060affdf73dfbed4322741fe1f2b43f9 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 14 Oct 2020 19:20:22 +0200 Subject: Do grabs out of order ...while extending the autotest to cover more complicated cases, such as grabbing again after show-hide and doing show-grab-hide-grab-show-grab. In fact some of these cases have not been working in Qt 5. Now the basic render loop is fixed up to support the all the combinations threaded does. Task-number: QTBUG-87399 Change-Id: Id01995bc3a2660b16cfb2f8bedc84becea0be1bb Reviewed-by: Andy Nichols --- src/quick/scenegraph/qsgrenderloop.cpp | 95 ++++++++++++++------------ src/quick/scenegraph/qsgthreadedrenderloop.cpp | 61 ++++++++--------- 2 files changed, 79 insertions(+), 77 deletions(-) (limited to 'src/quick/scenegraph') diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp index ea4f21facb..cf41868dc3 100644 --- a/src/quick/scenegraph/qsgrenderloop.cpp +++ b/src/quick/scenegraph/qsgrenderloop.cpp @@ -172,18 +172,20 @@ public: struct WindowData { WindowData() - : updatePending(false), - grabOnly(false), + : sampleCount(1), + updatePending(false), rhiDeviceLost(false), rhiDoomed(false) { } QElapsedTimer timeBetweenRenders; + int sampleCount; bool updatePending : 1; - bool grabOnly : 1; bool rhiDeviceLost : 1; bool rhiDoomed : 1; }; + bool ensureRhi(QQuickWindow *window, WindowData &data); + QHash m_windows; QOffscreenSurface *offscreenSurface = nullptr; @@ -191,7 +193,6 @@ public: QSGContext *sg; QSGRenderContext *rc; - QImage grabContent; bool m_inPolish = false; }; #endif @@ -447,22 +448,11 @@ bool QSGGuiThreadRenderLoop::eventFilter(QObject *watched, QEvent *event) return QObject::eventFilter(watched, event); } -void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) +bool QSGGuiThreadRenderLoop::ensureRhi(QQuickWindow *window, WindowData &data) { - if (!m_windows.contains(window)) - return; - - WindowData &data = const_cast(m_windows[window]); - bool alsoSwap = data.updatePending; - data.updatePending = false; - QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); - if (!cd->isRenderable()) - return; - - bool current = false; QSGRhiSupport *rhiSupport = QSGRhiSupport::instance(); - int rhiSampleCount = 1; + bool current = false; if (!rhi) { // This block below handles both the initial QRhi initialization and @@ -470,7 +460,7 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) // (reset) situation. if (data.rhiDoomed) // no repeated attempts if the initial attempt failed - return; + return false; if (!offscreenSurface) offscreenSurface = rhiSupport->maybeCreateOffscreenSurface(window); @@ -488,13 +478,13 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) // The sample count cannot vary between windows as we use the same // rendercontext for all of them. Decide it here and now. - rhiSampleCount = rhiSupport->chooseSampleCountForWindowWithRhi(window, rhi); + data.sampleCount = rhiSupport->chooseSampleCountForWindowWithRhi(window, rhi); cd->rhi = rhi; // set this early in case something hooked up to rc initialized() accesses it QSGDefaultRenderContext::InitParams rcParams; rcParams.rhi = rhi; - rcParams.sampleCount = rhiSampleCount; + rcParams.sampleCount = data.sampleCount; rcParams.initialSurfacePixelSize = window->size() * window->effectiveDevicePixelRatio(); rcParams.maybeSurface = window; cd->context->initialize(&rcParams); @@ -546,14 +536,14 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) if (depthBufferEnabled) { cd->depthStencilForSwapchain = rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, QSize(), - rhiSampleCount, + data.sampleCount, QRhiRenderBuffer::UsedWithSwapChainOnly); cd->swapchain->setDepthStencil(cd->depthStencilForSwapchain); } cd->swapchain->setWindow(window); qCDebug(QSG_LOG_INFO, "MSAA sample count for the swapchain is %d. Alpha channel requested = %s", - rhiSampleCount, alpha ? "yes" : "no"); - cd->swapchain->setSampleCount(rhiSampleCount); + data.sampleCount, alpha ? "yes" : "no"); + cd->swapchain->setSampleCount(data.sampleCount); cd->swapchain->setFlags(flags); cd->rpDescForSwapchain = cd->swapchain->newCompatibleRenderPassDescriptor(); cd->swapchain->setRenderPassDescriptor(cd->rpDescForSwapchain); @@ -561,6 +551,25 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) window->installEventFilter(this); } + return current; +} + +void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) +{ + if (!m_windows.contains(window)) + return; + + WindowData &data = const_cast(m_windows[window]); + bool alsoSwap = data.updatePending; + data.updatePending = false; + + QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); + if (!cd->isRenderable()) + return; + + if (!ensureRhi(window, data)) + return; + bool lastDirtyWindow = true; auto i = m_windows.constBegin(); while (i != m_windows.constEnd()) { @@ -571,16 +580,11 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) i++; } - if (!current) + cd->flushFrameSynchronousEvents(); + // Event delivery/processing triggered the window to be deleted or stop rendering. + if (!m_windows.contains(window)) return; - if (!data.grabOnly) { - cd->flushFrameSynchronousEvents(); - // Event delivery/processing triggered the window to be deleted or stop rendering. - if (!m_windows.contains(window)) - return; - } - QSize effectiveOutputSize; // always prefer what the surface tells us, not the QWindow if (cd->swapchain) { effectiveOutputSize = cd->swapchain->surfacePixelSize(); @@ -682,13 +686,6 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) QQuickProfiler::SceneGraphRenderLoopRender); Q_TRACE(QSG_swap_entry); - if (data.grabOnly) { - if (cd->swapchain) - grabContent = rhiSupport->grabAndBlockInCurrentFrame(rhi, cd->swapchain->currentFrameCommandBuffer()); - grabContent.setDevicePixelRatio(window->effectiveDevicePixelRatio()); - data.grabOnly = false; - } - const bool needsPresent = alsoSwap && window->isVisible(); if (cd->swapchain) { QRhi::EndFrameFlags flags; @@ -766,13 +763,25 @@ QImage QSGGuiThreadRenderLoop::grab(QQuickWindow *window) if (!m_windows.contains(window)) return QImage(); - m_windows[window].grabOnly = true; + if (!ensureRhi(window, m_windows[window])) + return QImage(); - renderWindow(window); + QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); + m_inPolish = true; + cd->polishItems(); + m_inPolish = false; + + // The assumption is that the swapchain is usable since on expose we do a + // renderWindow() so one cannot get to grab() without having done at least + // one on-screen frame. + cd->rhi->beginFrame(cd->swapchain); + cd->syncSceneGraph(); + cd->renderSceneGraph(window->size()); + QImage image = QSGRhiSupport::instance()->grabAndBlockInCurrentFrame(rhi, cd->swapchain->currentFrameCommandBuffer()); + cd->rhi->endFrame(cd->swapchain, QRhi::SkipPresent); - QImage grabbed = grabContent; - grabContent = QImage(); - return grabbed; + image.setDevicePixelRatio(window->effectiveDevicePixelRatio()); + return image; } void QSGGuiThreadRenderLoop::maybeUpdate(QQuickWindow *window) diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp index 48ae4a9116..a8668fd0c0 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp +++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp @@ -303,8 +303,8 @@ public: bool event(QEvent *) override; void run() override; - void syncAndRender(QImage *grabImage = nullptr); - void sync(bool inExpose, bool inGrab); + void syncAndRender(); + void sync(bool inExpose); void requestRepaint() { @@ -443,8 +443,17 @@ bool QSGRenderThread::event(QEvent *e) mutex.lock(); if (ce->window) { if (rhi) { - rhi->makeThreadLocalNativeContextCurrent(); - syncAndRender(ce->image); + QQuickWindowPrivate *cd = QQuickWindowPrivate::get(ce->window); + cd->rhi->makeThreadLocalNativeContextCurrent(); + // The assumption is that the swapchain is usable, because on + // expose the thread starts up and renders a frame so one cannot + // get here without having done at least one on-screen frame. + cd->rhi->beginFrame(cd->swapchain); + cd->syncSceneGraph(); + sgrc->endSync(); + cd->renderSceneGraph(ce->window->size()); + *ce->image = QSGRhiSupport::instance()->grabAndBlockInCurrentFrame(rhi, cd->swapchain->currentFrameCommandBuffer()); + cd->rhi->endFrame(cd->swapchain, QRhi::SkipPresent); } ce->image->setDevicePixelRatio(ce->window->effectiveDevicePixelRatio()); } @@ -559,11 +568,10 @@ void QSGRenderThread::invalidateGraphics(QQuickWindow *window, bool inDestructor Enters the mutex lock to make sure GUI is blocking and performs sync, then wakes GUI. */ -void QSGRenderThread::sync(bool inExpose, bool inGrab) +void QSGRenderThread::sync(bool inExpose) { qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "sync()"); - if (!inGrab) - mutex.lock(); + mutex.lock(); Q_ASSERT_X(wm->m_lockedForSync, "QSGRenderThread::sync()", "sync triggered on bad terms as gui is not already locked..."); @@ -612,7 +620,7 @@ void QSGRenderThread::sync(bool inExpose, bool inGrab) // 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) { + if (!inExpose) { qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- sync complete, waking Gui"); waitCondition.wakeOne(); mutex.unlock(); @@ -633,7 +641,7 @@ void QSGRenderThread::handleDeviceLoss() rhi = nullptr; } -void QSGRenderThread::syncAndRender(QImage *grabImage) +void QSGRenderThread::syncAndRender() { const bool profileFrames = QSG_LOG_TIME_RENDERLOOP().isDebugEnabled(); QElapsedTimer threadTimer; @@ -660,11 +668,9 @@ void QSGRenderThread::syncAndRender(QImage *grabImage) syncResultedInChanges = false; QQuickWindowPrivate *d = QQuickWindowPrivate::get(window); - const bool syncRequested = (pendingUpdate & SyncRequest) || grabImage; + const bool syncRequested = (pendingUpdate & SyncRequest); const bool exposeRequested = (pendingUpdate & ExposeRequest) == ExposeRequest; - const bool grabRequested = grabImage != nullptr; - if (!grabRequested) - pendingUpdate = 0; + pendingUpdate = 0; QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); // Begin the frame before syncing -> sync is where we may invoke @@ -715,7 +721,7 @@ 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 (syncRequested && !grabRequested) { + 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 beginFrame, wake Gui"); mutex.lock(); @@ -730,7 +736,7 @@ void QSGRenderThread::syncAndRender(QImage *grabImage) if (syncRequested) { qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- updatePending, doing sync"); - sync(exposeRequested, grabRequested); + sync(exposeRequested); } #ifndef QSG_NO_RENDER_TIMING if (profileFrames) @@ -759,11 +765,10 @@ void QSGRenderThread::syncAndRender(QImage *grabImage) // 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; + pendingUpdate = 0; // Advance render thread animations (from the QQuickAnimator subclasses). - if (animatorDriver->isRunning() && !grabRequested) { + if (animatorDriver->isRunning()) { d->animationController->lock(); animatorDriver->advance(); d->animationController->unlock(); @@ -787,20 +792,8 @@ void QSGRenderThread::syncAndRender(QImage *grabImage) 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 (grabRequested) { - Q_ASSERT(rhi && cd->swapchain); - *grabImage = QSGRhiSupport::instance()->grabAndBlockInCurrentFrame(rhi, cd->swapchain->currentFrameCommandBuffer()); - } - if (cd->swapchain) { - QRhi::EndFrameFlags flags; - if (grabRequested) - flags |= QRhi::SkipPresent; - QRhi::FrameOpResult frameResult = rhi->endFrame(cd->swapchain, flags); + QRhi::FrameOpResult frameResult = rhi->endFrame(cd->swapchain); if (frameResult != QRhi::FrameOpSuccess) { if (frameResult == QRhi::FrameOpDeviceLost) handleDeviceLoss(); @@ -810,9 +803,7 @@ void QSGRenderThread::syncAndRender(QImage *grabImage) QCoreApplication::postEvent(window, new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest))); } } - - if (!grabRequested) - d->fireFrameSwapped(); + d->fireFrameSwapped(); } else { Q_TRACE(QSG_render_exit); Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphRenderLoopFrame, @@ -1673,7 +1664,9 @@ QImage QSGThreadedRenderLoop::grab(QQuickWindow *window) qCDebug(QSG_LOG_RENDERLOOP, "- polishing items"); QQuickWindowPrivate *d = QQuickWindowPrivate::get(window); + m_inPolish = true; d->polishItems(); + m_inPolish = false; QImage result; w->thread->mutex.lock(); -- cgit v1.2.3