Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qsgrenderloop.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qsgrenderloop_p.h"
6#include "qsgrhisupport_p.h"
7#include <private/qquickanimatorcontroller_p.h>
8
9#include <QtCore/QCoreApplication>
10#include <QtCore/QElapsedTimer>
11#include <QtCore/QLibraryInfo>
12#include <QtCore/private/qabstractanimation_p.h>
13
14#include <QtGui/QOffscreenSurface>
15#include <QtGui/private/qguiapplication_p.h>
16#include <qpa/qplatformintegration.h>
17#include <QPlatformSurfaceEvent>
18
19#include <QtQml/private/qqmlglobal_p.h>
20
21#include <QtQuick/QQuickWindow>
22#include <QtQuick/private/qquickwindow_p.h>
23#include <QtQuick/private/qquickitem_p.h>
24#include <QtQuick/private/qsgcontext_p.h>
25#include <QtQuick/private/qsgrenderer_p.h>
26#include <private/qquickprofiler_p.h>
27#include <qtquick_tracepoints_p.h>
28
29#include <private/qsgrhishadereffectnode_p.h>
30
31#include <private/qsgdefaultrendercontext_p.h>
32
33#ifdef Q_OS_WIN
34#include <QtCore/qt_windows.h>
35#endif
36
38
39extern bool qsg_useConsistentTiming();
40
41#define ENABLE_DEFAULT_BACKEND
42
43Q_TRACE_POINT(qtquick, QSG_renderWindow_entry)
44Q_TRACE_POINT(qtquick, QSG_renderWindow_exit)
45Q_TRACE_POINT(qtquick, QSG_polishItems_entry)
46Q_TRACE_POINT(qtquick, QSG_polishItems_exit)
47Q_TRACE_POINT(qtquick, QSG_sync_entry)
48Q_TRACE_POINT(qtquick, QSG_sync_exit)
49Q_TRACE_POINT(qtquick, QSG_render_entry)
50Q_TRACE_POINT(qtquick, QSG_render_exit)
51Q_TRACE_POINT(qtquick, QSG_swap_entry)
52Q_TRACE_POINT(qtquick, QSG_swap_exit)
53
54
55/*
56 - Uses one QRhi per window. (and so each window has its own QOpenGLContext or ID3D11Device(Context) etc.)
57 - Animations are advanced using the standard timer (no custom animation
58 driver is installed), so QML animations run as expected even when vsync
59 throttling is broken.
60 */
61
62DEFINE_BOOL_CONFIG_OPTION(qmlNoThreadedRenderer, QML_BAD_GUI_RENDER_LOOP);
63DEFINE_BOOL_CONFIG_OPTION(qmlForceThreadedRenderer, QML_FORCE_THREADED_RENDERER); // Might trigger graphics driver threading bugs, use at own risk
64
65QSGRenderLoop *QSGRenderLoop::s_instance = nullptr;
66
70
72{
73 if (!s_instance)
74 return;
75 for (QQuickWindow *w : s_instance->windows()) {
77 if (wd->windowManager == s_instance) {
78 s_instance->windowDestroyed(w);
79 wd->windowManager = nullptr;
80 }
81 }
82 delete s_instance;
83 s_instance = nullptr;
84}
85
87{
88#ifdef ENABLE_DEFAULT_BACKEND
89 return QSGRhiSupport::instance()->windowSurfaceType();
90#else
92#endif
93}
94
96{
97 Q_ASSERT(job);
98#ifdef ENABLE_DEFAULT_BACKEND
101 if (cd->rhi)
103 job->run();
104#else
106 job->run();
107#endif
108 delete job;
109}
110
111#ifdef ENABLE_DEFAULT_BACKEND
113{
115public:
118
119 void show(QQuickWindow *window) override;
120 void hide(QQuickWindow *window) override;
121
122 void windowDestroyed(QQuickWindow *window) override;
123
125 void exposureChanged(QQuickWindow *window) override;
126 QImage grab(QQuickWindow *window) override;
127
128 void maybeUpdate(QQuickWindow *window) override;
129 void update(QQuickWindow *window) override { maybeUpdate(window); } // identical for this implementation.
130 void handleUpdateRequest(QQuickWindow *) override;
131
132 void releaseResources(QQuickWindow *) override;
133
134 QAnimationDriver *animationDriver() const override { return nullptr; }
135
136 QSGContext *sceneGraphContext() const override;
138
140 void handleDeviceLoss();
141 void teardownGraphics();
142
143 bool eventFilter(QObject *watched, QEvent *event) override;
144
160
162
163 QHash<QQuickWindow *, WindowData> m_windows;
164
167 mutable QSet<QSGRenderContext *> pendingRenderContexts;
168
169 bool m_inPolish = false;
170
172};
173#endif
174
176{
177 if (!s_instance) {
178
180
181 s_instance = QSGContext::createWindowManager();
182#ifdef ENABLE_DEFAULT_BACKEND
183 if (!s_instance) {
185
186 QSGRenderLoopType loopType;
187 if (rhiSupport->rhiBackend() != QRhi::OpenGLES2) {
188 loopType = ThreadedRenderLoop;
189 } else {
191 loopType = ThreadedRenderLoop;
192 else
193 loopType = BasicRenderLoop;
194 }
195
196 switch (rhiSupport->rhiBackend()) {
197 case QRhi::Null:
198 loopType = BasicRenderLoop;
199 break;
200
201 case QRhi::D3D11:
202 // The threaded loop's model may not be suitable for DXGI
203 // due to the possibility of having the main thread (with
204 // the Windows message pump) blocked while issuing a
205 // Present on the render thread. However, according to the
206 // docs this can be a problem for fullscreen swapchains
207 // only. So leave threaded enabled by default for now and
208 // revisit later if there are problems.
209 break;
210
211 default:
212 break;
213 }
214
215 // The environment variables can always override. This is good
216 // because in some situations it makes perfect sense to try out a
217 // render loop that is otherwise disabled by default.
218
219 if (qmlNoThreadedRenderer())
220 loopType = BasicRenderLoop;
221 else if (qmlForceThreadedRenderer())
222 loopType = ThreadedRenderLoop;
223
224 if (Q_UNLIKELY(qEnvironmentVariableIsSet("QSG_RENDER_LOOP"))) {
225 const QByteArray loopName = qgetenv("QSG_RENDER_LOOP");
226 if (loopName == "windows") {
227 qWarning("The 'windows' render loop is no longer supported. Using 'basic' instead.");
228 loopType = BasicRenderLoop;
229 } else if (loopName == "basic") {
230 loopType = BasicRenderLoop;
231 } else if (loopName == "threaded") {
232 loopType = ThreadedRenderLoop;
233 }
234 }
235
236 switch (loopType) {
237#if QT_CONFIG(thread)
239 qCDebug(QSG_LOG_INFO, "threaded render loop");
240 s_instance = new QSGThreadedRenderLoop();
241 break;
242#endif
243 default:
244 qCDebug(QSG_LOG_INFO, "basic render loop");
245 s_instance = new QSGGuiThreadRenderLoop();
246 break;
247 }
248 }
249#endif
251 }
252
253 return s_instance;
254}
255
257{
258 Q_ASSERT(!s_instance);
259 s_instance = instance;
260}
261
263{
264 // Must always be called on the gui thread.
265
266 // Guard for recursion; relevant due to the MessageBox() on Windows.
267 static QSet<QQuickWindow *> recurseGuard;
268 if (recurseGuard.contains(window))
269 return;
270
271 recurseGuard.insert(window);
272
273 QString translatedMessage;
274 QString untranslatedMessage;
276 &translatedMessage,
277 &untranslatedMessage);
278 // If there is a slot connected to the error signal, emit it and leave it to
279 // the application to do something with the message. If nothing is connected,
280 // show a message on our own and terminate.
281 const bool signalEmitted =
282 QQuickWindowPrivate::get(window)->emitError(QQuickWindow::ContextNotAvailable,
283 translatedMessage);
284#if defined(Q_OS_WIN)
285 if (!signalEmitted && !QLibraryInfo::isDebugBuild() && !GetConsoleWindow()) {
286 MessageBox(0, (LPCTSTR) translatedMessage.utf16(),
288 MB_OK | MB_ICONERROR);
289 }
290#endif // Q_OS_WIN
291 if (!signalEmitted)
292 qFatal("%s", qPrintable(untranslatedMessage));
293
294 recurseGuard.remove(window);
295}
296
297#ifdef ENABLE_DEFAULT_BACKEND
299{
301 QUnifiedTimer::instance(true)->setConsistentTiming(true);
302 qCDebug(QSG_LOG_INFO, "using fixed animation steps");
303 }
304
306}
307
313
315{
316 if (!m_windows.contains(window))
317 m_windows.insert(window, {});
318
319 m_windows[window].timeBetweenRenders.start();
321}
322
324{
326 cd->fireAboutToStop();
327 auto it = m_windows.find(window);
328 if (it != m_windows.end())
329 it->updatePending = false;
330}
331
333{
334 hide(window);
335
336 WindowData data = m_windows.value(window, {});
337 m_windows.remove(window);
338
340
341 if (data.rhi) {
342 // Direct OpenGL calls in user code need a current context, like
343 // when rendering; ensure this (no-op when not running on GL).
344 // Also works when there is no handle() anymore.
345 data.rhi->makeThreadLocalNativeContextCurrent();
346 }
347
348 if (d->swapchain) {
349 if (window->handle()) {
350 // We get here when exiting via QCoreApplication::quit() instead of
351 // through QWindow::close().
353 } else {
354 qWarning("QSGGuiThreadRenderLoop cleanup with QQuickWindow %p swapchain %p still alive, this should not happen.",
355 window, d->swapchain);
356 }
357 }
358
359 d->cleanupNodesOnShutdown();
360
361#if QT_CONFIG(quick_shadereffect)
363#endif
364
365 if (data.rc) {
366 data.rc->invalidate();
367 delete data.rc;
368 }
369
370 if (data.ownRhi)
371 QSGRhiSupport::instance()->destroyRhi(data.rhi, d->graphicsConfig);
372
373 d->rhi = nullptr;
374
375 d->animationController.reset();
376
377 if (m_windows.isEmpty()) {
378 delete offscreenSurface;
379 offscreenSurface = nullptr;
380 }
381}
382
384{
385 for (auto it = m_windows.begin(), itEnd = m_windows.end(); it != itEnd; ++it) {
386 if (it->rhi) {
387 QQuickWindowPrivate::get(it.key())->cleanupNodesOnShutdown();
388 if (it->rc)
389 it->rc->invalidate();
390 releaseSwapchain(it.key());
391 if (it->ownRhi)
392 QSGRhiSupport::instance()->destroyRhi(it->rhi, {});
393 it->rhi = nullptr;
394 }
395 }
396}
397
399{
400 qWarning("Graphics device lost, cleaning up scenegraph and releasing RHIs");
401
402 for (auto it = m_windows.begin(), itEnd = m_windows.end(); it != itEnd; ++it) {
403 if (!it->rhi || !it->rhi->isDeviceLost())
404 continue;
405
406 QQuickWindowPrivate::get(it.key())->cleanupNodesOnShutdown();
407
408 if (it->rc)
409 it->rc->invalidate();
410
411 releaseSwapchain(it.key());
412 it->rhiDeviceLost = true;
413
414 if (it->ownRhi)
415 QSGRhiSupport::instance()->destroyRhi(it->rhi, {});
416 it->rhi = nullptr;
417 }
418}
419
431
433{
434 switch (event->type()) {
436 // this is the proper time to tear down the swapchain (while the native window and surface are still around)
437 if (static_cast<QPlatformSurfaceEvent *>(event)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) {
438 QQuickWindow *w = qobject_cast<QQuickWindow *>(watched);
439 if (w)
441 // keep this filter on the window - needed for uncommon but valid
442 // sequences of calls like window->destroy(); window->show();
443 }
444 break;
445 default:
446 break;
447 }
448 return QObject::eventFilter(watched, event);
449}
450
452{
455 bool ok = data.rhi != nullptr;
456
457 if (!data.rhi) {
458 // This block below handles both the initial QRhi initialization and
459 // also the subsequent reinitialization attempts after a device lost
460 // (reset) situation.
461
462 if (data.rhiDoomed) // no repeated attempts if the initial attempt failed
463 return false;
464
465 if (!offscreenSurface)
467
468 const bool forcePreferSwRenderer = swRastFallbackDueToSwapchainFailure;
469 QSGRhiSupport::RhiCreateResult rhiResult = rhiSupport->createRhi(window, offscreenSurface, forcePreferSwRenderer);
470 data.rhi = rhiResult.rhi;
471 data.ownRhi = rhiResult.own;
472
473 if (data.rhi) {
474 data.rhiDeviceLost = false;
475
476 ok = true;
477 // We need to guarantee that sceneGraphInitialized is
478 // emitted with a context current, if running with OpenGL.
480
481 // The sample count cannot vary between windows as we use the same
482 // rendercontext for all of them. Decide it here and now.
483 data.sampleCount = rhiSupport->chooseSampleCountForWindowWithRhi(window, data.rhi);
484
485 cd->rhi = data.rhi; // set this early in case something hooked up to rc initialized() accesses it
486
488 rcParams.rhi = data.rhi;
489 rcParams.sampleCount = data.sampleCount;
490 rcParams.initialSurfacePixelSize = window->size() * window->effectiveDevicePixelRatio();
491 rcParams.maybeSurface = window;
492 cd->context->initialize(&rcParams);
493 } else {
494 if (!data.rhiDeviceLost) {
495 data.rhiDoomed = true;
497 }
498 // otherwise no error, just return false so that we will retry on a subsequent rendering attempt
499 }
500 }
501
502 if (data.rhi && !cd->swapchain) {
503 // if it's not the first window then the rhi is not yet stored to
504 // QQuickWindowPrivate, do it now
505 cd->rhi = data.rhi;
506
507 // Unlike the threaded render loop, we use the same rhi for all windows
508 // and so createRhi() is called only once. Certain initialization may
509 // need to be done on a per window basis still, so make sure it is done.
510 rhiSupport->prepareWindowForRhi(window);
511
512 QRhiSwapChain::Flags flags = QRhiSwapChain::UsedAsTransferSource; // may be used in a grab
513 const QSurfaceFormat requestedFormat = window->requestedFormat();
514
515 // QQ is always premul alpha. Decide based on alphaBufferSize in
516 // requestedFormat(). (the platform plugin can override format() but
517 // what matters here is what the application wanted, hence using the
518 // requested one)
519 const bool alpha = requestedFormat.alphaBufferSize() > 0;
520 if (alpha)
522
523 // Request NoVSync if swap interval was set to 0 (either by the app or
524 // by QSG_NO_VSYNC). What this means in practice is another question,
525 // but at least we tried.
526 if (requestedFormat.swapInterval() == 0) {
527 qCDebug(QSG_LOG_INFO, "Swap interval is 0, attempting to disable vsync when presenting.");
529 }
530
532 static bool depthBufferEnabled = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER");
533 if (depthBufferEnabled) {
535 QSize(),
536 data.sampleCount,
539 }
541 rhiSupport->applySwapChainFormat(cd->swapchain, window);
542 qCDebug(QSG_LOG_INFO, "MSAA sample count for the swapchain is %d. Alpha channel requested = %s",
543 data.sampleCount, alpha ? "yes" : "no");
544 cd->swapchain->setSampleCount(data.sampleCount);
548
549 window->installEventFilter(this);
550 }
551
552 if (!data.rc) {
553 QSGRenderContext *rc = cd->context;
555 data.rc = rc;
556 if (!data.rc)
557 qWarning("No QSGRenderContext for window %p, this should not happen", window);
558 }
559
560 return ok;
561}
562
564{
565 auto winDataIt = m_windows.find(window);
566 if (winDataIt == m_windows.end())
567 return;
568
569 WindowData &data(*winDataIt);
570 bool alsoSwap = data.updatePending;
571 data.updatePending = false;
572
574 if (!cd->isRenderable())
575 return;
576
577 if (!cd->updatesEnabled)
578 return;
579
580 if (!ensureRhi(window, data))
581 return;
582
583 bool lastDirtyWindow = true;
584 for (auto it = m_windows.cbegin(), end = m_windows.cend(); it != end; ++it) {
585 if (it->updatePending) {
586 lastDirtyWindow = false;
587 break;
588 }
589 }
590
592 // Event delivery/processing triggered the window to be deleted or stop rendering.
593 if (!m_windows.contains(window))
594 return;
595
596 QSize effectiveOutputSize; // always prefer what the surface tells us, not the QWindow
597 if (cd->swapchain) {
598 effectiveOutputSize = cd->swapchain->surfacePixelSize();
599 // An update request could still be delivered right before we get an
600 // unexpose. With Vulkan on Windows for example attempting to render
601 // leads to failures at this stage since the surface size is already 0.
602 if (effectiveOutputSize.isEmpty())
603 return;
604 }
605
606 Q_TRACE_SCOPE(QSG_renderWindow);
607 QElapsedTimer renderTimer;
608 qint64 renderTime = 0, syncTime = 0, polishTime = 0;
609 const bool profileFrames = QSG_LOG_TIME_RENDERLOOP().isDebugEnabled();
610 if (profileFrames)
611 renderTimer.start();
612 Q_TRACE(QSG_polishItems_entry);
613 Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishFrame);
614
615 m_inPolish = true;
616 cd->polishItems();
617 m_inPolish = false;
618
619 if (profileFrames)
620 polishTime = renderTimer.nsecsElapsed();
621
622 Q_TRACE(QSG_polishItems_exit);
623 Q_QUICK_SG_PROFILE_SWITCH(QQuickProfiler::SceneGraphPolishFrame,
624 QQuickProfiler::SceneGraphRenderLoopFrame,
625 QQuickProfiler::SceneGraphPolishPolish);
626 Q_TRACE(QSG_sync_entry);
627
628 emit window->afterAnimating();
629
630 // Begin the frame before syncing -> sync is where we may invoke
631 // updatePaintNode() on the items and they may want to do resource updates.
632 // Also relevant for applications that connect to the before/afterSynchronizing
633 // signals and want to do graphics stuff already there.
634 if (cd->swapchain) {
635 Q_ASSERT(!effectiveOutputSize.isEmpty());
637 const QSize previousOutputSize = cd->swapchain->currentPixelSize();
638 if (previousOutputSize != effectiveOutputSize || cd->swapchainJustBecameRenderable) {
640 qCDebug(QSG_LOG_RENDERLOOP, "just became exposed");
641
643 if (!cd->hasActiveSwapchain) {
644 if (data.rhi->isDeviceLost()) {
646 return;
647 } else if (previousOutputSize.isEmpty() && !swRastFallbackDueToSwapchainFailure && rhiSupport->attemptReinitWithSwRastUponFail()) {
648 qWarning("Failed to create swapchain."
649 " Retrying by requesting a software rasterizer, if applicable for the 3D API implementation.");
652 return;
653 }
654 }
655
658
659 if (cd->hasActiveSwapchain) {
660 // surface size atomicity: now that buildOrResize() succeeded,
661 // query the size that was used in there by the swapchain, and
662 // that is the size we will use while preparing the next frame.
663 effectiveOutputSize = cd->swapchain->currentPixelSize();
664 qCDebug(QSG_LOG_RENDERLOOP) << "rhi swapchain size" << effectiveOutputSize;
665 } else {
666 qWarning("Failed to build or resize swapchain");
667 }
668 }
669
670 emit window->beforeFrameBegin();
671
672 Q_ASSERT(data.rhi == cd->rhi);
673 QRhi::FrameOpResult frameResult = data.rhi->beginFrame(cd->swapchain);
674 if (frameResult != QRhi::FrameOpSuccess) {
675 if (frameResult == QRhi::FrameOpDeviceLost)
677 else if (frameResult == QRhi::FrameOpError)
678 qWarning("Failed to start frame");
679 // out of date is not worth warning about - it may happen even during resizing on some platforms
680 emit window->afterFrameEnd();
681 return;
682 }
683 }
684
685 // Enable external OpenGL rendering connected to one of the
686 // QQuickWindow signals (beforeSynchronizing, beforeRendering,
687 // etc.) to function like it did on the direct OpenGL path,
688 // i.e. ensure there is a context current, just in case.
689 data.rhi->makeThreadLocalNativeContextCurrent();
690
691 cd->syncSceneGraph();
692 if (lastDirtyWindow)
693 data.rc->endSync();
694
695 if (profileFrames)
696 syncTime = renderTimer.nsecsElapsed();
697
698 Q_TRACE(QSG_sync_exit);
699 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
700 QQuickProfiler::SceneGraphRenderLoopSync);
701 Q_TRACE(QSG_render_entry);
702
703 cd->renderSceneGraph();
704
705 if (profileFrames)
706 renderTime = renderTimer.nsecsElapsed();
707 Q_TRACE(QSG_render_exit);
708 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
709 QQuickProfiler::SceneGraphRenderLoopRender);
710 Q_TRACE(QSG_swap_entry);
711
712 const bool needsPresent = alsoSwap && window->isVisible();
713 double lastCompletedGpuTime = 0;
714 if (cd->swapchain) {
715 QRhi::EndFrameFlags flags;
716 if (!needsPresent)
718 QRhi::FrameOpResult frameResult = data.rhi->endFrame(cd->swapchain, flags);
719 if (frameResult != QRhi::FrameOpSuccess) {
720 if (frameResult == QRhi::FrameOpDeviceLost)
722 else if (frameResult == QRhi::FrameOpError)
723 qWarning("Failed to end frame");
724 } else {
725 lastCompletedGpuTime = cd->swapchain->currentFrameCommandBuffer()->lastCompletedGpuTime();
726 }
727 }
728 if (needsPresent)
729 cd->fireFrameSwapped();
730
731 emit window->afterFrameEnd();
732
733 qint64 swapTime = 0;
734 if (profileFrames)
735 swapTime = renderTimer.nsecsElapsed();
736
737 Q_TRACE(QSG_swap_exit);
738 Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame,
739 QQuickProfiler::SceneGraphRenderLoopSwap);
740
741 if (profileFrames) {
742 qCDebug(QSG_LOG_TIME_RENDERLOOP,
743 "[window %p][gui thread] syncAndRender: frame rendered in %dms, polish=%d, sync=%d, render=%d, swap=%d, perWindowFrameDelta=%d",
744 window,
745 int(swapTime / 1000000),
746 int(polishTime / 1000000),
747 int((syncTime - polishTime) / 1000000),
748 int((renderTime - syncTime) / 1000000),
749 int((swapTime - renderTime) / 1000000),
750 int(data.timeBetweenRenders.restart()));
751 if (!qFuzzyIsNull(lastCompletedGpuTime) && cd->graphicsConfig.timestampsEnabled()) {
752 qCDebug(QSG_LOG_TIME_RENDERLOOP, "[window %p][gui thread] syncAndRender: last retrieved GPU frame time was %.4f ms",
753 window,
754 lastCompletedGpuTime * 1000.0);
755 }
756 }
757
758 // Might have been set during syncSceneGraph()
759 if (data.updatePending)
761}
762
764{
766
767 // This is tricker than used to be. We want to detect having an empty
768 // surface size (which may be the case even when window->size() is
769 // non-empty, on some platforms with some graphics APIs!) as well as the
770 // case when the window just became "newly exposed" (e.g. after a
771 // minimize-restore on Windows, or when switching between fully obscured -
772 // not fully obscured on macOS)
773
774 if (!window->isExposed() || (wd->hasActiveSwapchain && wd->swapchain->surfacePixelSize().isEmpty()))
775 wd->hasRenderableSwapchain = false;
776
777 if (window->isExposed() && !wd->hasRenderableSwapchain && wd->hasActiveSwapchain
778 && !wd->swapchain->surfacePixelSize().isEmpty())
779 {
780 wd->hasRenderableSwapchain = true;
782 }
783
784 auto winDataIt = m_windows.find(window);
785 if (winDataIt != m_windows.end()) {
786 if (window->isExposed() && (!winDataIt->rhi || !wd->hasActiveSwapchain || wd->hasRenderableSwapchain)) {
787 winDataIt->updatePending = true;
789 }
790 }
791}
792
794{
795 auto winDataIt = m_windows.find(window);
796 if (winDataIt == m_windows.end())
797 return QImage();
798
799 if (!ensureRhi(window, *winDataIt))
800 return QImage();
801
803 m_inPolish = true;
804 cd->polishItems();
805 m_inPolish = false;
806
807 // The assumption is that the swapchain is usable since on expose we do a
808 // renderWindow() so one cannot get to grab() without having done at least
809 // one on-screen frame.
810 cd->rhi->beginFrame(cd->swapchain);
811 cd->rhi->makeThreadLocalNativeContextCurrent(); // for custom GL rendering before/during/after sync
812 cd->syncSceneGraph();
813 cd->renderSceneGraph();
814 QImage image = QSGRhiSupport::instance()->grabAndBlockInCurrentFrame(cd->rhi, cd->swapchain->currentFrameCommandBuffer());
816
817 image.setDevicePixelRatio(window->effectiveDevicePixelRatio());
818 return image;
819}
820
822{
823 auto winDataIt = m_windows.find(window);
824 if (winDataIt == m_windows.end())
825 return;
826
827 // Even if the window is not renderable,
828 // renderWindow() called on different window
829 // should not delete QSGTexture's
830 // from this unrenderable window.
831 winDataIt->updatePending = true;
832
834 if (!cd->isRenderable())
835 return;
836
837 // An updatePolish() implementation may call update() to get the QQuickItem
838 // dirtied. That's fine but it also leads to calling this function.
839 // Requesting another update is a waste then since the updatePolish() call
840 // will be followed up with a round of sync and render.
841 if (m_inPolish)
842 return;
843
844 window->requestUpdate();
845}
846
851
858
860{
861 // No full invalidation of the rendercontext, just clear some caches.
863 emit d->context->releaseCachedResourcesRequested();
864 if (d->renderer)
865 d->renderer->releaseCachedResources();
866#if QT_CONFIG(quick_shadereffect)
868#endif
869}
870
875
876#endif // ENABLE_DEFAULT_BACKEND
877
879
880#include "qsgrenderloop.moc"
881#include "moc_qsgrenderloop_p.cpp"
\inmodule QtCore
\inmodule QtCore
Definition qbytearray.h:57
QString applicationName
the name of this application
\inmodule QtCore
void start() noexcept
\typealias QElapsedTimer::Duration Synonym for std::chrono::nanoseconds.
\inmodule QtCore
Definition qcoreevent.h:45
@ PlatformSurface
Definition qcoreevent.h:278
static QPlatformIntegration * platformIntegration()
\inmodule QtGui
Definition qimage.h:37
static bool isDebugBuild() noexcept Q_DECL_CONST_FUNCTION
\inmodule QtCore
Definition qobject.h:103
virtual bool eventFilter(QObject *watched, QEvent *event)
Filters events if this object has been installed as an event filter for the watched object.
Definition qobject.cpp:1555
\inmodule QtGui
The QPlatformSurfaceEvent class is used to notify about native platform surface events....
Definition qevent.h:531
void flushFrameSynchronousEvents(QQuickWindow *win)
QRhiRenderPassDescriptor * rpDescForSwapchain
QQuickGraphicsConfiguration graphicsConfig
static QQuickWindowPrivate * get(QQuickWindow *c)
QSGRenderContext * context
QRhiRenderBuffer * depthStencilForSwapchain
static void rhiCreationFailureMessage(const QString &backendName, QString *translatedMessage, QString *untranslatedMessage)
QQuickDeliveryAgentPrivate * deliveryAgentPrivate() const
QRhiSwapChain * swapchain
QSGRenderLoop * windowManager
bool isRenderable() const
\qmltype Window \instantiates QQuickWindow \inqmlmodule QtQuick
@ UsedWithSwapChainOnly
Definition qrhi.h:1102
QRhi * rhi() const
Definition qrhi.cpp:3603
QSize currentPixelSize() const
Definition qrhi.h:1596
void setDepthStencil(QRhiRenderBuffer *ds)
Sets the renderbuffer ds for use as a depth-stencil buffer.
Definition qrhi.h:1588
virtual QRhiRenderPassDescriptor * newCompatibleRenderPassDescriptor()=0
virtual bool createOrResize()=0
Creates the swapchain if not already done and resizes the swapchain buffers to match the current size...
virtual QSize surfacePixelSize()=0
@ UsedAsTransferSource
Definition qrhi.h:1555
@ SurfaceHasPreMulAlpha
Definition qrhi.h:1552
void setSampleCount(int samples)
Sets the sample count.
Definition qrhi.h:1591
void setFlags(Flags f)
Sets the flags f.
Definition qrhi.h:1582
void setWindow(QWindow *window)
Sets the window.
Definition qrhi.h:1576
virtual QRhiCommandBuffer * currentFrameCommandBuffer()=0
void setRenderPassDescriptor(QRhiRenderPassDescriptor *desc)
Associates with the QRhiRenderPassDescriptor desc.
Definition qrhi.h:1594
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
Definition qrhi.h:1804
bool makeThreadLocalNativeContextCurrent()
With OpenGL this makes the OpenGL context current on the current thread.
Definition qrhi.cpp:10158
QRhiRenderBuffer * newRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize, int sampleCount=1, QRhiRenderBuffer::Flags flags={}, QRhiTexture::Format backingFormatHint=QRhiTexture::UnknownFormat)
Definition qrhi.cpp:10535
@ Null
Definition qrhi.h:1807
@ D3D11
Definition qrhi.h:1810
@ OpenGLES2
Definition qrhi.h:1809
FrameOpResult beginFrame(QRhiSwapChain *swapChain, BeginFrameFlags flags={})
Starts a new frame targeting the next available buffer of swapChain.
Definition qrhi.cpp:10745
QRhiSwapChain * newSwapChain()
Definition qrhi.cpp:10693
FrameOpResult endFrame(QRhiSwapChain *swapChain, EndFrameFlags flags={})
Ends, commits, and presents a frame that was started in the last beginFrame() on swapChain.
Definition qrhi.cpp:10780
@ SkipPresent
Definition qrhi.h:1882
FrameOpResult
Describes the result of operations that can have a soft failure.
Definition qrhi.h:1824
@ FrameOpSuccess
Definition qrhi.h:1825
@ FrameOpDeviceLost
Definition qrhi.h:1828
@ FrameOpError
Definition qrhi.h:1826
\inmodule QtCore
Definition qrunnable.h:18
virtual void run()=0
Implement this pure virtual function in your subclass.
The QSGContext holds the scene graph entry points for one QML engine.
virtual QSGRenderContext * createRenderContext()=0
static QSGRenderLoop * createWindowManager()
Calls into the scene graph adaptation if available and creates a hardware specific window manager.
static QSGContext * createDefaultContext()
Creates a default scene graph context for the current hardware.
QAnimationDriver * animationDriver() const override
void update(QQuickWindow *window) override
void renderWindow(QQuickWindow *window)
void releaseSwapchain(QQuickWindow *window)
void show(QQuickWindow *window) override
bool eventFilter(QObject *watched, QEvent *event) override
Filters events if this object has been installed as an event filter for the watched object.
QSGRenderContext * createRenderContext(QSGContext *) const override
void windowDestroyed(QQuickWindow *window) override
bool ensureRhi(QQuickWindow *window, WindowData &data)
QOffscreenSurface * offscreenSurface
void releaseResources(QQuickWindow *) override
void exposureChanged(QQuickWindow *window) override
QSGContext * sceneGraphContext() const override
QHash< QQuickWindow *, WindowData > m_windows
void handleUpdateRequest(QQuickWindow *) override
void hide(QQuickWindow *window) override
QSet< QSGRenderContext * > pendingRenderContexts
QImage grab(QQuickWindow *window) override
void maybeUpdate(QQuickWindow *window) override
virtual void initialize(const InitParams *params)
static void setInstance(QSGRenderLoop *instance)
virtual void postJob(QQuickWindow *window, QRunnable *job)
static QSGRenderLoop * instance()
virtual QSurface::SurfaceType windowSurfaceType() const
virtual int flags() const
void handleContextCreationFailure(QQuickWindow *window)
static void cleanup()
static void garbageCollectMaterialTypeCache(void *materialTypeCacheKey)
static void resetMaterialTypeCache(void *materialTypeCacheKey)
QOffscreenSurface * maybeCreateOffscreenSurface(QWindow *window)
void applySwapChainFormat(QRhiSwapChain *scWithWindowSet, QQuickWindow *window)
static void checkEnvQSgInfo()
static int chooseSampleCountForWindowWithRhi(QWindow *window, QRhi *rhi)
bool attemptReinitWithSwRastUponFail() const
void prepareWindowForRhi(QQuickWindow *window)
RhiCreateResult createRhi(QQuickWindow *window, QSurface *offscreenSurface, bool forcePreferSwRenderer=false)
QRhi::Implementation rhiBackend() const
static QSGRhiSupport * instance()
bool remove(const T &value)
Definition qset.h:63
iterator insert(const T &value)
Definition qset.h:155
\inmodule QtCore
Definition qsize.h:25
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
const ushort * utf16() const
Returns the QString as a '\0\'-terminated array of unsigned shorts.
Definition qstring.cpp:6995
The QSurfaceFormat class represents the format of a QSurface. \inmodule QtGui.
int alphaBufferSize() const
Get the size in bits of the alpha channel of the color buffer.
int swapInterval() const
Returns the swap interval.
SurfaceType
The SurfaceType enum describes what type of surface this is.
Definition qsurface.h:30
@ RasterSurface
Definition qsurface.h:31
static QUnifiedTimer * instance()
qDeleteAll(list.begin(), list.end())
QSet< QString >::iterator it
Combined button and popup list for selecting options.
Definition image.cpp:4
#define Q_UNLIKELY(x)
void qAddPostRoutine(QtCleanUpFunction p)
bool qFuzzyIsNull(qfloat16 f) noexcept
Definition qfloat16.h:349
#define qWarning
Definition qlogging.h:166
#define qFatal
Definition qlogging.h:168
#define qCDebug(category,...)
GLfloat GLfloat GLfloat w
[0]
GLuint GLuint end
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLbitfield flags
struct _cl_event * event
GLfloat GLfloat GLfloat alpha
Definition qopenglext.h:418
#define DEFINE_BOOL_CONFIG_OPTION(name, var)
#define Q_QUICK_SG_PROFILE_END(Type, position)
#define Q_QUICK_SG_PROFILE_SWITCH(Type1, Type2, position)
#define Q_QUICK_SG_PROFILE_RECORD(Type, position)
#define Q_QUICK_SG_PROFILE_START(Type)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QT_BEGIN_NAMESPACE bool qsg_useConsistentTiming()
QSGRenderLoopType
@ BasicRenderLoop
@ ThreadedRenderLoop
#define qPrintable(string)
Definition qstring.h:1531
Q_CORE_EXPORT QByteArray qgetenv(const char *varName)
Q_CORE_EXPORT bool qEnvironmentVariableIsEmpty(const char *varName) noexcept
Q_CORE_EXPORT bool qEnvironmentVariableIsSet(const char *varName) noexcept
#define Q_OBJECT
#define emit
#define Q_UNUSED(x)
#define Q_TRACE_SCOPE(x,...)
Definition qtrace_p.h:146
#define Q_TRACE(x,...)
Definition qtrace_p.h:144
#define Q_TRACE_POINT(provider, tracepoint,...)
Definition qtrace_p.h:232
long long qint64
Definition qtypes.h:60
view show()
[18] //! [19]
edit hide()
aWidget window() -> setWindowTitle("New Window Title")
[2]