<div dir="ltr">HI, all!<div><br></div><div>This patch needs testing from real painters who happen to use Overview docker during their normal work. Please pay attention if Krita starts doing any hiccups when you paint with very quick sequential strokes (of about 200ms) :)</div><div><br></div><div>Looking at the code, there should be regressions, but who knows :)</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Mon, Jul 29, 2024 at 11:26 AM Dmitry Kazakov <<a href="mailto:null@kde.org">null@kde.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Git commit 572c64858d1b506ca1500b776cc49dfd65105ddd by Dmitry Kazakov.<br>
Committed on 29/07/2024 at 09:26.<br>
Pushed by dkazakov into branch 'master'.<br>
<br>
Reduce delay of Overlay, Histogram and Layer Thumbnails updates<br>
<br>
It was reported that the update delay of the aforementioned previews<br>
is too slow in Krita, so this patch tries to address the issue<br>
by reducing the idle watcher delay to 50 ms.<br>
<br>
It seems like these days such huge delay is not really needed, because<br>
all the tasks are split into multithreaded jobs, which are **cancelled**<br>
when the user actually starts doing any sane action. When the code was<br>
originally written, Overview and Layer Thumbnails strokes were not<br>
multithreaded, so they couldn't be cancelled with decent granularity.<br>
Now it is not the case anymore.<br>
<br>
The patch needs testing from real painters who use Overview docker on<br>
a regular basis.<br>
<br>
The patch is based on the work of Zhiqi Yao, who proposed this MR:<br>
<a href="https://invent.kde.org/graphics/krita/-/merge_requests/2180" rel="noreferrer" target="_blank">https://invent.kde.org/graphics/krita/-/merge_requests/2180</a><br>
<br>
This patch reduces the scope of the original MR to<br>
Overlay, Histogram and Layer Thumbnails tasks only, not changing<br>
the idle watcher's defaults. It also adds a small sanity check that<br>
would help us catching issues in the future.<br>
<br>
<a href="mailto:CC%3Akimageshop@kde.org" target="_blank">CC:kimageshop@kde.org</a><br>
<br>
M +49 -0 libs/ui/KisIdleTaskStrokeStrategy.cpp<br>
M +6 -0 libs/ui/KisIdleTaskStrokeStrategy.h<br>
M +5 -0 libs/ui/KisIdleTasksManager.cpp<br>
<br>
<a href="https://invent.kde.org/graphics/krita/-/commit/572c64858d1b506ca1500b776cc49dfd65105ddd" rel="noreferrer" target="_blank">https://invent.kde.org/graphics/krita/-/commit/572c64858d1b506ca1500b776cc49dfd65105ddd</a><br>
<br>
diff --git a/libs/ui/KisIdleTaskStrokeStrategy.cpp b/libs/ui/KisIdleTaskStrokeStrategy.cpp<br>
index 915163649e5..4ffeea30d38 100644<br>
--- a/libs/ui/KisIdleTaskStrokeStrategy.cpp<br>
+++ b/libs/ui/KisIdleTaskStrokeStrategy.cpp<br>
@@ -39,9 +39,30 @@ KisStrokeStrategy *KisIdleTaskStrokeStrategy::createLodClone(int levelOfDetail)<br>
return new KisSimpleStrokeStrategy(QLatin1String("KisIdleTaskStrokeStrategy_FakeLodN"));<br>
}<br>
<br>
+void KisIdleTaskStrokeStrategy::initStrokeCallback()<br>
+{<br>
+ m_idleStrokeTime.start();<br>
+}<br>
+<br>
void KisIdleTaskStrokeStrategy::finishStrokeCallback()<br>
{<br>
emit sigIdleTaskFinished();<br>
+<br>
+ /**<br>
+ * Just a small sanity check if idle tasks don't occupy too much time<br>
+ * and don't interfere with user's workflow. Theoretically, even if the<br>
+ * time consumed is too high, it may still be acceptable if the task is<br>
+ * split in multiple jobs, which would be cancelled but the user's action.<br>
+ */<br>
+ const qint64 elapsedTime = m_idleStrokeTime.elapsed();<br>
+<br>
+ if (elapsedTime > preferredIdleTaskMaximumTime()) {<br>
+ qWarning() << "WARNING: idle task consumed more than" <<<br>
+ preferredIdleTaskMaximumTime() <<<br>
+ "ms, it can cause visible distractions to the user";<br>
+ qWarning() << "WARNING: time consumed in" << id() <<<br>
+ "is" << elapsedTime << "ms";<br>
+ }<br>
}<br>
<br>
QWeakPointer<boost::none_t> KisIdleTaskStrokeStrategy::idleTaskCookie()<br>
@@ -50,3 +71,31 @@ QWeakPointer<boost::none_t> KisIdleTaskStrokeStrategy::idleTaskCookie()<br>
m_idleTaskCookie.reset(new boost::none_t(boost::none));<br>
return m_idleTaskCookie;<br>
}<br>
+<br>
+int KisIdleTaskStrokeStrategy::preferredIdleTaskMaximumTime()<br>
+{<br>
+ /**<br>
+ * Sometimes (unless split into multiple jobs) idle tasks can interfere<br>
+ * with user actions. We have a sanity check that checks if the tasks are<br>
+ * quick enough to not interfere into the user's actions. This time limit<br>
+ * is the maximum "allowed" idle task size.<br>
+ *<br>
+ * Definition of "allowed" here is not strict, since the tasks are not<br>
+ * cancelled when reaching the limit. Just a warning is spit into the<br>
+ * terminal.<br>
+ */<br>
+<br>
+ return 200; // ms<br>
+}<br>
+<br>
+int KisIdleTaskStrokeStrategy::preferredIdleWatcherInterval()<br>
+{<br>
+ /**<br>
+ * Preferred idle watcher interval for checks of the image idleness<br>
+ * state. Idle watcher checks the image four times before starting an<br>
+ * idle task, so the actual idle task delay is<br>
+ * `4 * preferredIdleWatcherInterval()` milliseconds.<br>
+ */<br>
+ return 50; // ms<br>
+}<br>
+<br>
diff --git a/libs/ui/KisIdleTaskStrokeStrategy.h b/libs/ui/KisIdleTaskStrokeStrategy.h<br>
index d71c7b43a79..3532fd18a47 100644<br>
--- a/libs/ui/KisIdleTaskStrokeStrategy.h<br>
+++ b/libs/ui/KisIdleTaskStrokeStrategy.h<br>
@@ -13,6 +13,7 @@<br>
#include <boost/none.hpp><br>
#include <kis_types.h><br>
#include <KisRunnableBasedStrokeStrategy.h><br>
+#include <QElapsedTimer><br>
<br>
/**<br>
* A base class for strategies used in "idle tasks". Such strategy<br>
@@ -31,7 +32,11 @@ public:<br>
KisStrokeStrategy* createLodClone(int levelOfDetail) override;<br>
QWeakPointer<boost::none_t> idleTaskCookie();<br>
<br>
+ static int preferredIdleTaskMaximumTime();<br>
+ static int preferredIdleWatcherInterval();<br>
+<br>
protected:<br>
+ void initStrokeCallback() override;<br>
void finishStrokeCallback() override;<br>
<br>
Q_SIGNALS:<br>
@@ -39,6 +44,7 @@ Q_SIGNALS:<br>
<br>
private:<br>
QSharedPointer<boost::none_t> m_idleTaskCookie;<br>
+ QElapsedTimer m_idleStrokeTime;<br>
};<br>
<br>
using KisIdleTaskStrokeStrategyFactory = std::function<KisIdleTaskStrokeStrategy*(KisImageSP image)>;<br>
diff --git a/libs/ui/KisIdleTasksManager.cpp b/libs/ui/KisIdleTasksManager.cpp<br>
index 63de2cab56c..1954d1f21de 100644<br>
--- a/libs/ui/KisIdleTasksManager.cpp<br>
+++ b/libs/ui/KisIdleTasksManager.cpp<br>
@@ -21,6 +21,11 @@ struct TaskStruct {<br>
<br>
struct KisIdleTasksManager::Private<br>
{<br>
+ Private()<br>
+ : idleWatcher(KisIdleTaskStrokeStrategy::preferredIdleWatcherInterval())<br>
+ {<br>
+ }<br>
+<br>
KisImageWSP image;<br>
KisIdleWatcher idleWatcher;<br>
QVector<TaskStruct> tasks;<br>
</blockquote></div><br clear="all"><div><br></div><span class="gmail_signature_prefix">-- </span><br><div dir="ltr" class="gmail_signature">Dmitry Kazakov</div>