diff options
Diffstat (limited to 'src/quick/scenegraph/qsgrenderloop.cpp')
-rw-r--r-- | src/quick/scenegraph/qsgrenderloop.cpp | 392 |
1 files changed, 219 insertions, 173 deletions
diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp index 077814b1f4..3c8da0fc6a 100644 --- a/src/quick/scenegraph/qsgrenderloop.cpp +++ b/src/quick/scenegraph/qsgrenderloop.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qsgrenderloop_p.h" #include "qsgthreadedrenderloop_p.h" @@ -76,11 +40,23 @@ extern bool qsg_useConsistentTiming(); #define ENABLE_DEFAULT_BACKEND +Q_TRACE_POINT(qtquick, QSG_renderWindow_entry) +Q_TRACE_POINT(qtquick, QSG_renderWindow_exit) +Q_TRACE_POINT(qtquick, QSG_polishItems_entry) +Q_TRACE_POINT(qtquick, QSG_polishItems_exit) +Q_TRACE_POINT(qtquick, QSG_sync_entry) +Q_TRACE_POINT(qtquick, QSG_sync_exit) +Q_TRACE_POINT(qtquick, QSG_render_entry) +Q_TRACE_POINT(qtquick, QSG_render_exit) +Q_TRACE_POINT(qtquick, QSG_swap_entry) +Q_TRACE_POINT(qtquick, QSG_swap_exit) + + /* - - Uses one QRhi (and so OpenGL Context, Vulkan device, etc.) to render multiple windows. - - This assumes multiple screens can use the OpenGL context. - - Animations are advanced using the standard timer, so QML animations run as expected even when - vsync throttling is broken. + - Uses one QRhi per window. (and so each window has its own QOpenGLContext or ID3D11Device(Context) etc.) + - Animations are advanced using the standard timer (no custom animation + driver is installed), so QML animations run as expected even when vsync + throttling is broken. */ DEFINE_BOOL_CONFIG_OPTION(qmlNoThreadedRenderer, QML_BAD_GUI_RENDER_LOOP); @@ -105,11 +81,6 @@ void QSGRenderLoop::cleanup() } delete s_instance; s_instance = nullptr; - -#ifdef ENABLE_DEFAULT_BACKEND - QSGRhiSupport::cleanupDefaultVulkanInstance(); - QSGRhiProfileConnection::instance()->cleanup(); -#endif } QSurface::SurfaceType QSGRenderLoop::windowSurfaceType() const @@ -163,22 +134,25 @@ public: QAnimationDriver *animationDriver() const override { return nullptr; } QSGContext *sceneGraphContext() const override; - QSGRenderContext *createRenderContext(QSGContext *) const override { return rc; } + QSGRenderContext *createRenderContext(QSGContext *) const override; void releaseSwapchain(QQuickWindow *window); void handleDeviceLoss(); + void teardownGraphics(); bool eventFilter(QObject *watched, QEvent *event) override; struct WindowData { WindowData() - : sampleCount(1), - updatePending(false), + : updatePending(false), rhiDeviceLost(false), rhiDoomed(false) { } + QRhi *rhi = nullptr; + bool ownRhi = true; + QSGRenderContext *rc = nullptr; QElapsedTimer timeBetweenRenders; - int sampleCount; + int sampleCount = 1; bool updatePending : 1; bool rhiDeviceLost : 1; bool rhiDoomed : 1; @@ -189,11 +163,12 @@ public: QHash<QQuickWindow *, WindowData> m_windows; QOffscreenSurface *offscreenSurface = nullptr; - QRhi *rhi = nullptr; QSGContext *sg; - QSGRenderContext *rc; + mutable QSet<QSGRenderContext *> pendingRenderContexts; bool m_inPolish = false; + + bool swRastFallbackDueToSwapchainFailure = false; }; #endif @@ -209,7 +184,7 @@ QSGRenderLoop *QSGRenderLoop::instance() QSGRhiSupport *rhiSupport = QSGRhiSupport::instance(); QSGRenderLoopType loopType; - if (rhiSupport->isRhiEnabled() && rhiSupport->rhiBackend() != QRhi::OpenGLES2) { + if (rhiSupport->rhiBackend() != QRhi::OpenGLES2) { loopType = ThreadedRenderLoop; } else { if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL)) @@ -218,25 +193,23 @@ QSGRenderLoop *QSGRenderLoop::instance() loopType = BasicRenderLoop; } - if (rhiSupport->isRhiEnabled()) { - switch (rhiSupport->rhiBackend()) { - case QRhi::Null: - loopType = BasicRenderLoop; - break; - - case QRhi::D3D11: - // The threaded loop's model may not be suitable for DXGI - // due to the possibility of having the main thread (with - // the Windows message pump) blocked while issuing a - // Present on the render thread. However, according to the - // docs this can be a problem for fullscreen swapchains - // only. So leave threaded enabled by default for now and - // revisit later if there are problems. - break; - - default: - break; - } + switch (rhiSupport->rhiBackend()) { + case QRhi::Null: + loopType = BasicRenderLoop; + break; + + case QRhi::D3D11: + // The threaded loop's model may not be suitable for DXGI + // due to the possibility of having the main thread (with + // the Windows message pump) blocked while issuing a + // Present on the render thread. However, according to the + // docs this can be a problem for fullscreen swapchains + // only. So leave threaded enabled by default for now and + // revisit later if there are problems. + break; + + default: + break; } // The environment variables can always override. This is good @@ -288,6 +261,15 @@ void QSGRenderLoop::setInstance(QSGRenderLoop *instance) void QSGRenderLoop::handleContextCreationFailure(QQuickWindow *window) { + // Must always be called on the gui thread. + + // Guard for recursion; relevant due to the MessageBox() on Windows. + static QSet<QQuickWindow *> recurseGuard; + if (recurseGuard.contains(window)) + return; + + recurseGuard.insert(window); + QString translatedMessage; QString untranslatedMessage; QQuickWindowPrivate::rhiCreationFailureMessage(QSGRhiSupport::instance()->rhiBackendName(), @@ -308,6 +290,8 @@ void QSGRenderLoop::handleContextCreationFailure(QQuickWindow *window) #endif // Q_OS_WIN if (!signalEmitted) qFatal("%s", qPrintable(untranslatedMessage)); + + recurseGuard.remove(window); } #ifdef ENABLE_DEFAULT_BACKEND @@ -319,20 +303,20 @@ QSGGuiThreadRenderLoop::QSGGuiThreadRenderLoop() } sg = QSGContext::createDefaultContext(); - rc = sg->createRenderContext(); } QSGGuiThreadRenderLoop::~QSGGuiThreadRenderLoop() { - delete rc; + qDeleteAll(pendingRenderContexts); delete sg; } void QSGGuiThreadRenderLoop::show(QQuickWindow *window) { - m_windows[window] = WindowData(); - m_windows[window].timeBetweenRenders.start(); + if (!m_windows.contains(window)) + m_windows.insert(window, {}); + m_windows[window].timeBetweenRenders.start(); maybeUpdate(window); } @@ -340,27 +324,27 @@ void QSGGuiThreadRenderLoop::hide(QQuickWindow *window) { QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); cd->fireAboutToStop(); - if (m_windows.contains(window)) - m_windows[window].updatePending = false; + auto it = m_windows.find(window); + if (it != m_windows.end()) + it->updatePending = false; } void QSGGuiThreadRenderLoop::windowDestroyed(QQuickWindow *window) { - m_windows.remove(window); hide(window); + + WindowData data = m_windows.value(window, {}); + m_windows.remove(window); + QQuickWindowPrivate *d = QQuickWindowPrivate::get(window); - if (rhi) { + if (data.rhi) { // Direct OpenGL calls in user code need a current context, like // when rendering; ensure this (no-op when not running on GL). // Also works when there is no handle() anymore. - rhi->makeThreadLocalNativeContextCurrent(); + data.rhi->makeThreadLocalNativeContextCurrent(); } -#if QT_CONFIG(quick_shadereffect) - QSGRhiShaderEffectNode::cleanupMaterialTypeCache(); -#endif - if (d->swapchain) { if (window->handle()) { // We get here when exiting via QCoreApplication::quit() instead of @@ -373,53 +357,69 @@ void QSGGuiThreadRenderLoop::windowDestroyed(QQuickWindow *window) } d->cleanupNodesOnShutdown(); - if (m_windows.size() == 0) { - rc->invalidate(); - d->rhi = nullptr; - QSGRhiSupport::instance()->destroyRhi(rhi); - rhi = nullptr; + +#if QT_CONFIG(quick_shadereffect) + QSGRhiShaderEffectNode::resetMaterialTypeCache(window); +#endif + + if (data.rc) { + data.rc->invalidate(); + delete data.rc; + } + + if (data.ownRhi) + QSGRhiSupport::instance()->destroyRhi(data.rhi, d->graphicsConfig); + + d->rhi = nullptr; + + d->animationController.reset(); + + if (m_windows.isEmpty()) { delete offscreenSurface; offscreenSurface = nullptr; } +} - d->animationController.reset(); +void QSGGuiThreadRenderLoop::teardownGraphics() +{ + for (auto it = m_windows.begin(), itEnd = m_windows.end(); it != itEnd; ++it) { + if (it->rhi) { + QQuickWindowPrivate::get(it.key())->cleanupNodesOnShutdown(); + if (it->rc) + it->rc->invalidate(); + releaseSwapchain(it.key()); + if (it->ownRhi) + QSGRhiSupport::instance()->destroyRhi(it->rhi, {}); + it->rhi = nullptr; + } + } } void QSGGuiThreadRenderLoop::handleDeviceLoss() { - if (!rhi || !rhi->isDeviceLost()) - return; + qWarning("Graphics device lost, cleaning up scenegraph and releasing RHIs"); - qWarning("Graphics device lost, cleaning up scenegraph and releasing RHI"); + for (auto it = m_windows.begin(), itEnd = m_windows.end(); it != itEnd; ++it) { + if (!it->rhi || !it->rhi->isDeviceLost()) + continue; - for (auto it = m_windows.constBegin(), itEnd = m_windows.constEnd(); it != itEnd; ++it) QQuickWindowPrivate::get(it.key())->cleanupNodesOnShutdown(); - rc->invalidate(); + if (it->rc) + it->rc->invalidate(); - for (auto it = m_windows.begin(), itEnd = m_windows.end(); it != itEnd; ++it) { releaseSwapchain(it.key()); it->rhiDeviceLost = true; - } - QSGRhiSupport::instance()->destroyRhi(rhi); - rhi = nullptr; + if (it->ownRhi) + QSGRhiSupport::instance()->destroyRhi(it->rhi, {}); + it->rhi = nullptr; + } } void QSGGuiThreadRenderLoop::releaseSwapchain(QQuickWindow *window) { QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window); - - // Unlike the threaded render loop, this one reuses the same rendercontext - // for all QQuickWindows for the entire lifetime of the render loop. (and - // even if it wouldn't, special cases like destroy() - show() on the - // QQuickWindow still needed this) - // Therefore the renderer, if there is one, needs to be notified about the - // destruction of certain resources because they may be referenced from - // per-rendercontext data structures. - if (wd->renderer) - wd->renderer->invalidatePipelineCacheDependency(wd->rpDescForSwapchain); - delete wd->rpDescForSwapchain; wd->rpDescForSwapchain = nullptr; delete wd->swapchain; @@ -452,9 +452,9 @@ bool QSGGuiThreadRenderLoop::ensureRhi(QQuickWindow *window, WindowData &data) { QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); QSGRhiSupport *rhiSupport = QSGRhiSupport::instance(); - bool current = false; + bool ok = data.rhi != nullptr; - if (!rhi) { + if (!data.rhi) { // This block below handles both the initial QRhi initialization and // also the subsequent reinitialization attempts after a device lost // (reset) situation. @@ -465,25 +465,27 @@ bool QSGGuiThreadRenderLoop::ensureRhi(QQuickWindow *window, WindowData &data) if (!offscreenSurface) offscreenSurface = rhiSupport->maybeCreateOffscreenSurface(window); - rhi = rhiSupport->createRhi(window, offscreenSurface); - - if (rhi) { - if (rhiSupport->isProfilingRequested()) - QSGRhiProfileConnection::instance()->initialize(rhi); + const bool forcePreferSwRenderer = swRastFallbackDueToSwapchainFailure; + QSGRhiSupport::RhiCreateResult rhiResult = rhiSupport->createRhi(window, offscreenSurface, forcePreferSwRenderer); + data.rhi = rhiResult.rhi; + data.ownRhi = rhiResult.own; + if (data.rhi) { data.rhiDeviceLost = false; - current = true; - rhi->makeThreadLocalNativeContextCurrent(); + ok = true; + // We need to guarantee that sceneGraphInitialized is + // emitted with a context current, if running with OpenGL. + data.rhi->makeThreadLocalNativeContextCurrent(); // The sample count cannot vary between windows as we use the same // rendercontext for all of them. Decide it here and now. - data.sampleCount = rhiSupport->chooseSampleCountForWindowWithRhi(window, rhi); + data.sampleCount = rhiSupport->chooseSampleCountForWindowWithRhi(window, data.rhi); - cd->rhi = rhi; // set this early in case something hooked up to rc initialized() accesses it + cd->rhi = data.rhi; // set this early in case something hooked up to rc initialized() accesses it QSGDefaultRenderContext::InitParams rcParams; - rcParams.rhi = rhi; + rcParams.rhi = data.rhi; rcParams.sampleCount = data.sampleCount; rcParams.initialSurfacePixelSize = window->size() * window->effectiveDevicePixelRatio(); rcParams.maybeSurface = window; @@ -493,22 +495,14 @@ bool QSGGuiThreadRenderLoop::ensureRhi(QQuickWindow *window, WindowData &data) data.rhiDoomed = true; handleContextCreationFailure(window); } - // otherwise no error, will retry on a subsequent rendering attempt + // otherwise no error, just return false so that we will retry on a subsequent rendering attempt } - } else { - current = true; - // 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(); } - if (rhi && !cd->swapchain) { + if (data.rhi && !cd->swapchain) { // if it's not the first window then the rhi is not yet stored to // QQuickWindowPrivate, do it now - cd->rhi = rhi; + cd->rhi = data.rhi; // Unlike the threaded render loop, we use the same rhi for all windows // and so createRhi() is called only once. Certain initialization may @@ -526,21 +520,25 @@ bool QSGGuiThreadRenderLoop::ensureRhi(QQuickWindow *window, WindowData &data) if (alpha) flags |= QRhiSwapChain::SurfaceHasPreMulAlpha; - // Request NoVSync if swap interval was set to 0. What this means in - // practice is another question, but at least we tried. - if (requestedFormat.swapInterval() == 0) + // Request NoVSync if swap interval was set to 0 (either by the app or + // by QSG_NO_VSYNC). What this means in practice is another question, + // but at least we tried. + if (requestedFormat.swapInterval() == 0) { + qCDebug(QSG_LOG_INFO, "Swap interval is 0, attempting to disable vsync when presenting."); flags |= QRhiSwapChain::NoVSync; + } - cd->swapchain = rhi->newSwapChain(); + cd->swapchain = data.rhi->newSwapChain(); static bool depthBufferEnabled = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER"); if (depthBufferEnabled) { - cd->depthStencilForSwapchain = rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, - QSize(), - data.sampleCount, - QRhiRenderBuffer::UsedWithSwapChainOnly); + cd->depthStencilForSwapchain = data.rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, + QSize(), + data.sampleCount, + QRhiRenderBuffer::UsedWithSwapChainOnly); cd->swapchain->setDepthStencil(cd->depthStencilForSwapchain); } cd->swapchain->setWindow(window); + rhiSupport->applySwapChainFormat(cd->swapchain, window); qCDebug(QSG_LOG_INFO, "MSAA sample count for the swapchain is %d. Alpha channel requested = %s", data.sampleCount, alpha ? "yes" : "no"); cd->swapchain->setSampleCount(data.sampleCount); @@ -551,15 +549,24 @@ bool QSGGuiThreadRenderLoop::ensureRhi(QQuickWindow *window, WindowData &data) window->installEventFilter(this); } - return current; + if (!data.rc) { + QSGRenderContext *rc = cd->context; + pendingRenderContexts.remove(rc); + data.rc = rc; + if (!data.rc) + qWarning("No QSGRenderContext for window %p, this should not happen", window); + } + + return ok; } void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) { - if (!m_windows.contains(window)) + auto winDataIt = m_windows.find(window); + if (winDataIt == m_windows.end()) return; - WindowData &data = const_cast<WindowData &>(m_windows[window]); + WindowData &data(*winDataIt); bool alsoSwap = data.updatePending; data.updatePending = false; @@ -567,20 +574,21 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) if (!cd->isRenderable()) return; + if (!cd->updatesEnabled) + return; + if (!ensureRhi(window, data)) return; bool lastDirtyWindow = true; - auto i = m_windows.constBegin(); - while (i != m_windows.constEnd()) { - if (i.value().updatePending) { + for (auto it = m_windows.cbegin(), end = m_windows.cend(); it != end; ++it) { + if (it->updatePending) { lastDirtyWindow = false; break; } - i++; } - cd->flushFrameSynchronousEvents(); + cd->deliveryAgentPrivate()->flushFrameSynchronousEvents(window); // Event delivery/processing triggered the window to be deleted or stop rendering. if (!m_windows.contains(window)) return; @@ -625,15 +633,24 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) // signals and want to do graphics stuff already there. if (cd->swapchain) { Q_ASSERT(!effectiveOutputSize.isEmpty()); + QSGRhiSupport *rhiSupport = QSGRhiSupport::instance(); const QSize previousOutputSize = cd->swapchain->currentPixelSize(); if (previousOutputSize != effectiveOutputSize || cd->swapchainJustBecameRenderable) { if (cd->swapchainJustBecameRenderable) qCDebug(QSG_LOG_RENDERLOOP, "just became exposed"); cd->hasActiveSwapchain = cd->swapchain->createOrResize(); - if (!cd->hasActiveSwapchain && rhi->isDeviceLost()) { - handleDeviceLoss(); - return; + if (!cd->hasActiveSwapchain) { + if (data.rhi->isDeviceLost()) { + handleDeviceLoss(); + return; + } 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(); + return; + } } cd->swapchainJustBecameRenderable = false; @@ -652,8 +669,8 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) emit window->beforeFrameBegin(); - Q_ASSERT(rhi == cd->rhi); - QRhi::FrameOpResult frameResult = rhi->beginFrame(cd->swapchain); + Q_ASSERT(data.rhi == cd->rhi); + QRhi::FrameOpResult frameResult = data.rhi->beginFrame(cd->swapchain); if (frameResult != QRhi::FrameOpSuccess) { if (frameResult == QRhi::FrameOpDeviceLost) handleDeviceLoss(); @@ -665,9 +682,15 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) } } + // Enable external OpenGL rendering connected to one of the + // QQuickWindow signals (beforeSynchronizing, beforeRendering, + // etc.) to function like it did on the direct OpenGL path, + // i.e. ensure there is a context current, just in case. + data.rhi->makeThreadLocalNativeContextCurrent(); + cd->syncSceneGraph(); if (lastDirtyWindow) - rc->endSync(); + data.rc->endSync(); if (profileFrames) syncTime = renderTimer.nsecsElapsed(); @@ -677,7 +700,7 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) QQuickProfiler::SceneGraphRenderLoopSync); Q_TRACE(QSG_render_entry); - cd->renderSceneGraph(window->size(), effectiveOutputSize); + cd->renderSceneGraph(); if (profileFrames) renderTime = renderTimer.nsecsElapsed(); @@ -687,16 +710,19 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) Q_TRACE(QSG_swap_entry); const bool needsPresent = alsoSwap && window->isVisible(); + double lastCompletedGpuTime = 0; if (cd->swapchain) { QRhi::EndFrameFlags flags; if (!needsPresent) flags |= QRhi::SkipPresent; - QRhi::FrameOpResult frameResult = rhi->endFrame(cd->swapchain, flags); + QRhi::FrameOpResult frameResult = data.rhi->endFrame(cd->swapchain, flags); if (frameResult != QRhi::FrameOpSuccess) { if (frameResult == QRhi::FrameOpDeviceLost) handleDeviceLoss(); else if (frameResult == QRhi::FrameOpError) qWarning("Failed to end frame"); + } else { + lastCompletedGpuTime = cd->swapchain->currentFrameCommandBuffer()->lastCompletedGpuTime(); } } if (needsPresent) @@ -722,10 +748,13 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) int((renderTime - syncTime) / 1000000), int((swapTime - renderTime) / 1000000), int(data.timeBetweenRenders.restart())); + if (!qFuzzyIsNull(lastCompletedGpuTime) && cd->graphicsConfig.timestampsEnabled()) { + qCDebug(QSG_LOG_TIME_RENDERLOOP, "[window %p][gui thread] syncAndRender: last retrieved GPU frame time was %.4f ms", + window, + lastCompletedGpuTime * 1000.0); + } } - QSGRhiProfileConnection::instance()->send(rhi); - // Might have been set during syncSceneGraph() if (data.updatePending) maybeUpdate(window); @@ -752,18 +781,22 @@ void QSGGuiThreadRenderLoop::exposureChanged(QQuickWindow *window) wd->swapchainJustBecameRenderable = true; } - if (window->isExposed() && (!rhi || !wd->hasActiveSwapchain || wd->hasRenderableSwapchain)) { - m_windows[window].updatePending = true; - renderWindow(window); + auto winDataIt = m_windows.find(window); + if (winDataIt != m_windows.end()) { + if (window->isExposed() && (!winDataIt->rhi || !wd->hasActiveSwapchain || wd->hasRenderableSwapchain)) { + winDataIt->updatePending = true; + renderWindow(window); + } } } QImage QSGGuiThreadRenderLoop::grab(QQuickWindow *window) { - if (!m_windows.contains(window)) + auto winDataIt = m_windows.find(window); + if (winDataIt == m_windows.end()) return QImage(); - if (!ensureRhi(window, m_windows[window])) + if (!ensureRhi(window, *winDataIt)) return QImage(); QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); @@ -775,9 +808,10 @@ QImage QSGGuiThreadRenderLoop::grab(QQuickWindow *window) // renderWindow() so one cannot get to grab() without having done at least // one on-screen frame. cd->rhi->beginFrame(cd->swapchain); + cd->rhi->makeThreadLocalNativeContextCurrent(); // for custom GL rendering before/during/after sync cd->syncSceneGraph(); - cd->renderSceneGraph(window->size()); - QImage image = QSGRhiSupport::instance()->grabAndBlockInCurrentFrame(rhi, cd->swapchain->currentFrameCommandBuffer()); + cd->renderSceneGraph(); + QImage image = QSGRhiSupport::instance()->grabAndBlockInCurrentFrame(cd->rhi, cd->swapchain->currentFrameCommandBuffer()); cd->rhi->endFrame(cd->swapchain, QRhi::SkipPresent); image.setDevicePixelRatio(window->effectiveDevicePixelRatio()); @@ -786,16 +820,17 @@ QImage QSGGuiThreadRenderLoop::grab(QQuickWindow *window) void QSGGuiThreadRenderLoop::maybeUpdate(QQuickWindow *window) { - QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); - if (!m_windows.contains(window)) + auto winDataIt = m_windows.find(window); + if (winDataIt == m_windows.end()) return; // Even if the window is not renderable, // renderWindow() called on different window // should not delete QSGTexture's // from this unrenderable window. - m_windows[window].updatePending = true; + winDataIt->updatePending = true; + QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); if (!cd->isRenderable()) return; @@ -814,12 +849,23 @@ QSGContext *QSGGuiThreadRenderLoop::sceneGraphContext() const return sg; } +QSGRenderContext *QSGGuiThreadRenderLoop::createRenderContext(QSGContext *sg) const +{ + QSGRenderContext *rc = sg->createRenderContext(); + pendingRenderContexts.insert(rc); + return rc; +} + void QSGGuiThreadRenderLoop::releaseResources(QQuickWindow *w) { // No full invalidation of the rendercontext, just clear some caches. QQuickWindowPrivate *d = QQuickWindowPrivate::get(w); + emit d->context->releaseCachedResourcesRequested(); if (d->renderer) d->renderer->releaseCachedResources(); +#if QT_CONFIG(quick_shadereffect) + QSGRhiShaderEffectNode::garbageCollectMaterialTypeCache(w); +#endif } void QSGGuiThreadRenderLoop::handleUpdateRequest(QQuickWindow *window) @@ -829,7 +875,7 @@ void QSGGuiThreadRenderLoop::handleUpdateRequest(QQuickWindow *window) #endif // ENABLE_DEFAULT_BACKEND +QT_END_NAMESPACE + #include "qsgrenderloop.moc" #include "moc_qsgrenderloop_p.cpp" - -QT_END_NAMESPACE |