From 361aa053e1e65d483b16179c6795698d481f1e21 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 22 Nov 2022 13:13:38 +0100 Subject: [PATCH] kwinquickeffect: Use an asynchronous incubator for view creation Creating views is the main thing blocking quick effect activation. Rather than blocking until everything is created, we can use QQmlIncubator so we create views asynchronously. This allows KWin to do other things while views are being created. This is mostly relevant for multiscreen setups where we create a view per screen. --- src/libkwineffects/kwinquickeffect.cpp | 62 +++++++++++++++++++------- 1 file changed, 47 insertions(+), 15 deletions(-) diff --git a/src/libkwineffects/kwinquickeffect.cpp b/src/libkwineffects/kwinquickeffect.cpp index aad187e13a..df7d1c8369 100644 --- a/src/libkwineffects/kwinquickeffect.cpp +++ b/src/libkwineffects/kwinquickeffect.cpp @@ -6,9 +6,11 @@ #include "kwinquickeffect.h" +#include "logging_p.h" #include "sharedqmlengine.h" #include +#include #include #include #include @@ -16,6 +18,24 @@ namespace KWin { +class QuickSceneViewIncubator : public QQmlIncubator +{ +public: + QuickSceneViewIncubator(const std::function &statusChangedCallback) + : QQmlIncubator(QQmlIncubator::Asynchronous) + , m_statusChangedCallback(statusChangedCallback) + { + } + + void statusChanged(QQmlIncubator::Status status) override + { + m_statusChangedCallback(this); + } + +private: + std::function m_statusChangedCallback; +}; + class QuickSceneEffectPrivate { public: @@ -28,6 +48,7 @@ public: SharedQmlEngine::Ptr qmlEngine; std::unique_ptr qmlComponent; QUrl source; + std::map> incubators; std::map> views; QPointer mouseImplicitGrab; bool running = false; @@ -336,29 +357,39 @@ void QuickSceneEffect::handleScreenAdded(EffectScreen *screen) void QuickSceneEffect::handleScreenRemoved(EffectScreen *screen) { d->views.erase(screen); + d->incubators.erase(screen); } void QuickSceneEffect::addScreen(EffectScreen *screen) { - QuickSceneView *view = new QuickSceneView(this, screen); auto properties = initialProperties(screen); - properties["width"] = view->geometry().width(); - properties["height"] = view->geometry().height(); - view->setRootItem(qobject_cast(d->qmlComponent->createWithInitialProperties(properties))); - // we need the focus always set to the view of activescreen at first, and changed only upon user interaction - if (view->contentItem()) { - view->contentItem()->setFocus(false); - } - view->setAutomaticRepaint(false); + properties["width"] = screen->geometry().width(); + properties["height"] = screen->geometry().height(); - connect(view, &QuickSceneView::repaintNeeded, this, [view]() { - effects->addRepaint(view->geometry()); + auto incubator = new QuickSceneViewIncubator([this, screen](QuickSceneViewIncubator *incubator) { + if (incubator->isReady()) { + auto view = new QuickSceneView(this, screen); + view->setRootItem(qobject_cast(incubator->object())); + if (view->contentItem()) { + view->contentItem()->setFocus(false); + } + view->setAutomaticRepaint(false); + connect(view, &QuickSceneView::repaintNeeded, this, [view]() { + effects->addRepaint(view->geometry()); + }); + connect(view, &QuickSceneView::renderRequested, view, &QuickSceneView::scheduleRepaint); + connect(view, &QuickSceneView::sceneChanged, view, &QuickSceneView::scheduleRepaint); + view->scheduleRepaint(); + d->views[screen].reset(view); + } else if (incubator->isError()) { + qCWarning(LIBKWINEFFECTS) << "Could not create a view for QML file" << d->qmlComponent->url(); + qCWarning(LIBKWINEFFECTS) << incubator->errors(); + } }); - connect(view, &QuickSceneView::renderRequested, view, &QuickSceneView::scheduleRepaint); - connect(view, &QuickSceneView::sceneChanged, view, &QuickSceneView::scheduleRepaint); - view->scheduleRepaint(); - d->views[screen].reset(view); + incubator->setInitialProperties(properties); + d->incubators[screen].reset(incubator); + d->qmlComponent->create(*incubator); } void QuickSceneEffect::startInternal() @@ -423,6 +454,7 @@ void QuickSceneEffect::stopInternal() disconnect(effects, &EffectsHandler::screenAdded, this, &QuickSceneEffect::handleScreenAdded); disconnect(effects, &EffectsHandler::screenRemoved, this, &QuickSceneEffect::handleScreenRemoved); + d->incubators.clear(); d->views.clear(); d->dummyWindow.reset(); d->running = false;