179QSGRenderLoop *QSGRenderLoop::instance()
183 QSGRhiSupport::checkEnvQSgInfo();
185 s_instance = QSGContext::createWindowManager();
188 QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
190 QSGRenderLoopType loopType;
191 if (rhiSupport->rhiBackend() != QRhi::OpenGLES2) {
192 loopType = ThreadedRenderLoop;
194 if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL))
195 loopType = ThreadedRenderLoop;
197 loopType = BasicRenderLoop;
200 switch (rhiSupport->rhiBackend()) {
202 loopType = BasicRenderLoop;
223 if (qmlNoThreadedRenderer())
224 loopType = BasicRenderLoop;
225 else if (qmlForceThreadedRenderer())
226 loopType = ThreadedRenderLoop;
228 if (Q_UNLIKELY(qEnvironmentVariableIsSet(
"QSG_RENDER_LOOP"))) {
229 const QByteArray loopName = qgetenv(
"QSG_RENDER_LOOP");
230 if (loopName ==
"windows") {
231 qWarning(
"The 'windows' render loop is no longer supported. Using 'basic' instead.");
232 loopType = BasicRenderLoop;
233 }
else if (loopName ==
"basic") {
234 loopType = BasicRenderLoop;
235 }
else if (loopName ==
"threaded") {
236 loopType = ThreadedRenderLoop;
242 case ThreadedRenderLoop:
243 qCDebug(QSG_LOG_INFO,
"threaded render loop");
244 s_instance =
new QSGThreadedRenderLoop();
248 qCDebug(QSG_LOG_INFO,
"basic render loop");
249 s_instance =
new QSGGuiThreadRenderLoop();
254 qAddPostRoutine(QSGRenderLoop::cleanup);
266void QSGRenderLoop::handleContextCreationFailure(QQuickWindow *window)
271 static QSet<QQuickWindow *> recurseGuard;
272 if (recurseGuard.contains(window))
275 recurseGuard.insert(window);
277 QString translatedMessage;
278 QString untranslatedMessage;
279 QQuickWindowPrivate::rhiCreationFailureMessage(QSGRhiSupport::instance()->rhiBackendName(),
281 &untranslatedMessage);
285 const bool signalEmitted =
286 QQuickWindowPrivate::get(window)->emitError(QQuickWindow::ContextNotAvailable,
289 if (!signalEmitted && !QLibraryInfo::isDebugBuild() && !GetConsoleWindow()) {
290 MessageBox(0, (LPCTSTR) translatedMessage.utf16(),
291 (LPCTSTR)(QCoreApplication::applicationName().utf16()),
292 MB_OK | MB_ICONERROR);
296 qFatal(
"%s", qPrintable(untranslatedMessage));
298 recurseGuard.remove(window);
461 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
462 QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
463 bool ok = data
.rhi !=
nullptr;
477 QSGRhiSupport::RhiCreateResult rhiResult = rhiSupport->createRhi(window, offscreenSurface, forcePreferSwRenderer);
478 data
.rhi = rhiResult.rhi;
487 data
.rhi->makeThreadLocalNativeContextCurrent();
491 data
.sampleCount = rhiSupport->chooseSampleCountForWindowWithRhi(window, data.rhi);
495 QSGDefaultRenderContext::InitParams rcParams;
496 rcParams.rhi = data
.rhi;
498 rcParams.initialSurfacePixelSize = window->size() * window->effectiveDevicePixelRatio();
499 rcParams.maybeSurface = window;
500 cd->context->initialize(&rcParams);
504 handleContextCreationFailure(window);
510 if (data
.rhi && !cd->swapchain) {
518 rhiSupport->prepareWindowForRhi(window);
520 QRhiSwapChain::Flags flags = QRhiSwapChain::UsedAsTransferSource;
521 const QSurfaceFormat requestedFormat = window->requestedFormat();
527 const bool alpha = requestedFormat.alphaBufferSize() > 0;
529 flags |= QRhiSwapChain::SurfaceHasPreMulAlpha;
534 if (requestedFormat.swapInterval() == 0) {
535 qCDebug(QSG_LOG_INFO,
"Swap interval is 0, attempting to disable vsync when presenting.");
536 flags |= QRhiSwapChain::NoVSync;
539 cd->swapchain = data
.rhi->newSwapChain();
540 static bool depthBufferEnabled = qEnvironmentVariableIsEmpty(
"QSG_NO_DEPTH_BUFFER");
541 if (depthBufferEnabled) {
542 cd->depthStencilForSwapchain = data
.rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil,
545 QRhiRenderBuffer::UsedWithSwapChainOnly
546 | QSGRhiSupport::depthStencilBufferFlags());
547 cd->swapchain->setDepthStencil(cd->depthStencilForSwapchain);
549 cd->swapchain->setWindow(window);
550 rhiSupport->applySwapChainFormat(cd->swapchain, window);
551 qCDebug(QSG_LOG_INFO,
"MSAA sample count for the swapchain is %d. Alpha channel requested = %s",
554 cd->swapchain->setFlags(flags);
555 cd->rpDescForSwapchain = cd->swapchain->newCompatibleRenderPassDescriptor();
556 cd->swapchain->setRenderPassDescriptor(cd->rpDescForSwapchain);
558 window->installEventFilter(
this);
562 QSGRenderContext *rc = cd->context;
563 pendingRenderContexts.remove(rc);
566 qWarning(
"No QSGRenderContext for window %p, this should not happen", window);
574 auto winDataIt = m_windows.find(window);
575 if (winDataIt == m_windows.end())
582 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
583 if (!cd->isRenderable())
586 if (!cd->updatesEnabled)
592 cd->deliveryAgentPrivate()->flushFrameSynchronousEvents(window);
594 if (!m_windows.contains(window))
597 QSize effectiveOutputSize;
599 effectiveOutputSize = cd->swapchain->surfacePixelSize();
603 if (effectiveOutputSize.isEmpty())
607 Q_TRACE_SCOPE(QSG_renderWindow);
608 QElapsedTimer renderTimer;
609 qint64 renderTime = 0, syncTime = 0, polishTime = 0;
610 const bool profileFrames = QSG_LOG_TIME_RENDERLOOP().isDebugEnabled();
613 Q_TRACE(QSG_polishItems_entry);
614 Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishFrame);
621 polishTime = renderTimer.nsecsElapsed();
623 Q_TRACE(QSG_polishItems_exit);
624 Q_QUICK_SG_PROFILE_SWITCH(QQuickProfiler::SceneGraphPolishFrame,
625 QQuickProfiler::SceneGraphRenderLoopFrame,
626 QQuickProfiler::SceneGraphPolishPolish);
627 Q_TRACE(QSG_sync_entry);
629 emit window->afterAnimating();
636 Q_ASSERT(!effectiveOutputSize.isEmpty());
637 QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
638 const QSize previousOutputSize = cd->swapchain->currentPixelSize();
639 if (previousOutputSize != effectiveOutputSize || cd->swapchainJustBecameRenderable || data
.swapchainOutOfDate) {
640 if (cd->swapchainJustBecameRenderable)
641 qCDebug(QSG_LOG_RENDERLOOP,
"just became exposed");
643 qCDebug(QSG_LOG_RENDERLOOP,
"swapchain out of date");
645 cd->hasActiveSwapchain = cd->swapchain->createOrResize();
646 if (!cd->hasActiveSwapchain) {
647 if (data
.rhi->isDeviceLost()) {
650 }
else if (previousOutputSize.isEmpty() && !swRastFallbackDueToSwapchainFailure && rhiSupport->attemptReinitWithSwRastUponFail()) {
651 qWarning(
"Failed to create swapchain."
652 " Retrying by requesting a software rasterizer, if applicable for the 3D API implementation.");
659 cd->swapchainJustBecameRenderable =
false;
661 cd->hasRenderableSwapchain = cd->hasActiveSwapchain;
663 if (cd->hasActiveSwapchain) {
667 effectiveOutputSize = cd->swapchain->currentPixelSize();
668 qCDebug(QSG_LOG_RENDERLOOP) <<
"rhi swapchain size" << effectiveOutputSize;
670 qWarning(
"Failed to build or resize swapchain");
674 emit window->beforeFrameBegin();
676 Q_ASSERT(data
.rhi == cd->rhi);
677 QRhi::FrameOpResult frameResult = data
.rhi->beginFrame(cd->swapchain);
678 if (frameResult != QRhi::FrameOpSuccess) {
679 if (frameResult == QRhi::FrameOpDeviceLost)
681 else if (frameResult == QRhi::FrameOpError)
682 qWarning(
"Failed to start frame");
684 emit window->afterFrameEnd();
685 if (frameResult == QRhi::FrameOpSwapChainOutOfDate) {
697 data
.rhi->makeThreadLocalNativeContextCurrent();
699 cd->syncSceneGraph();
703 syncTime = renderTimer.nsecsElapsed();
705 Q_TRACE(QSG_sync_exit);
706 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
707 QQuickProfiler::SceneGraphRenderLoopSync);
708 Q_TRACE(QSG_render_entry);
710 cd->renderSceneGraph();
713 renderTime = renderTimer.nsecsElapsed();
714 Q_TRACE(QSG_render_exit);
715 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
716 QQuickProfiler::SceneGraphRenderLoopRender);
717 Q_TRACE(QSG_swap_entry);
719 const bool needsPresent = alsoSwap && window->isVisible();
720 double lastCompletedGpuTime = 0;
722 QRhi::EndFrameFlags flags;
724 flags |= QRhi::SkipPresent;
725 QRhi::FrameOpResult frameResult = data
.rhi->endFrame(cd->swapchain, flags);
726 if (frameResult != QRhi::FrameOpSuccess) {
727 if (frameResult == QRhi::FrameOpDeviceLost)
729 else if (frameResult == QRhi::FrameOpError)
730 qWarning(
"Failed to end frame");
731 if (frameResult == QRhi::FrameOpSwapChainOutOfDate) {
736 lastCompletedGpuTime = cd->swapchain->currentFrameCommandBuffer()->lastCompletedGpuTime();
740 cd->fireFrameSwapped();
742 emit window->afterFrameEnd();
746 swapTime = renderTimer.nsecsElapsed();
748 Q_TRACE(QSG_swap_exit);
749 Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame,
750 QQuickProfiler::SceneGraphRenderLoopSwap);
753 qCDebug(QSG_LOG_TIME_RENDERLOOP,
754 "[window %p][gui thread] syncAndRender: frame rendered in %dms, polish=%d, sync=%d, render=%d, swap=%d, perWindowFrameDelta=%d",
756 int(swapTime / 1000000),
757 int(polishTime / 1000000),
758 int((syncTime - polishTime) / 1000000),
759 int((renderTime - syncTime) / 1000000),
760 int((swapTime - renderTime) / 1000000),
761 int(data.timeBetweenRenders.restart()));
762 if (!qFuzzyIsNull(lastCompletedGpuTime) && cd->graphicsConfig.timestampsEnabled()) {
763 qCDebug(QSG_LOG_TIME_RENDERLOOP,
"[window %p][gui thread] syncAndRender: last retrieved GPU frame time was %.4f ms",
765 lastCompletedGpuTime * 1000.0);