aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/scenegraph/qsgrenderloop.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/scenegraph/qsgrenderloop.cpp')
-rw-r--r--src/quick/scenegraph/qsgrenderloop.cpp392
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