Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_simple_update_queue.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2010 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
8
9#include <QMutexLocker>
10#include <QVector>
11
12#include "kis_image_config.h"
14#include "kis_spontaneous_job.h"
15
16
17//#define ENABLE_DEBUG_JOIN
18//#define ENABLE_ACCUMULATOR
19
20#ifdef ENABLE_DEBUG_JOIN
21 #define DEBUG_JOIN(baseRect, newRect, alpha) \
22 dbgKrita << "Two rects were joined:\t" \
23 << (baseRect) << "+" << (newRect) << "->" \
24 << ((baseRect) | (newRect)) << "(" << alpha << ")"
25
26#else
27 #define DEBUG_JOIN(baseRect, newRect, alpha)
28#endif /* ENABLE_DEBUG_JOIN */
29
30
31#ifdef ENABLE_ACCUMULATOR
32 #define DECLARE_ACCUMULATOR() static qreal _baseAmount=0, _newAmount=0
33 #define ACCUMULATOR_ADD(baseAmount, newAmount) \
34 do {_baseAmount += baseAmount; _newAmount += newAmount;} while (0)
35 #define ACCUMULATOR_DEBUG() \
36 dbgKrita << "Accumulated alpha:" << _newAmount / _baseAmount
37#else
38 #define DECLARE_ACCUMULATOR()
39 #define ACCUMULATOR_ADD(baseAmount, newAmount)
40 #define ACCUMULATOR_DEBUG()
41#endif /* ENABLE_ACCUMULATOR */
42
43
45 : m_overrideLevelOfDetail(-1)
46{
48}
49
51{
52 QMutexLocker locker(&m_lock);
53
54 while (!m_spontaneousJobsList.isEmpty()) {
55 delete m_spontaneousJobsList.takeLast();
56 }
57}
58
60{
61 QMutexLocker locker(&m_lock);
62
63 KisImageConfig config(true);
64
67
71}
72
77
79{
80 updaterContext.lock();
81
82 while(updaterContext.hasSpareThread() &&
83 processOneJob(updaterContext));
84
85 updaterContext.unlock();
86}
87
89{
90 QMutexLocker locker(&m_lock);
91
94 bool jobAdded = false;
95
96 int currentLevelOfDetail = updaterContext.currentLevelOfDetail();
97
98 while(iter.hasNext()) {
99 item = iter.next();
100
101 if ((currentLevelOfDetail < 0 || currentLevelOfDetail == item->levelOfDetail()) &&
102 !item->checksumValid()) {
103
105 item->recalculate(item->requestedRect());
107 }
108
109 if ((currentLevelOfDetail < 0 || currentLevelOfDetail == item->levelOfDetail()) &&
110 updaterContext.isJobAllowed(item)) {
111
112 updaterContext.addMergeJob(item);
113 iter.remove();
114 jobAdded = true;
115 break;
116 }
117 }
118
119 if (jobAdded) return true;
120
121 if (!m_spontaneousJobsList.isEmpty()) {
132 qint32 numMergeJobs;
133 qint32 numStrokeJobs;
134 updaterContext.getJobsSnapshot(numMergeJobs, numStrokeJobs);
135
137 if (!numMergeJobs && !numStrokeJobs &&
138 (currentLevelOfDetail < 0 || currentLevelOfDetail == job->levelOfDetail())) {
139
140 updaterContext.addSpontaneousJob(job);
141 m_spontaneousJobsList.removeFirst();
142 jobAdded = true;
143 }
144 }
145
146 return jobAdded;
147}
148
149void KisSimpleUpdateQueue::addUpdateJob(KisNodeSP node, const QVector<QRect> &rects, const QRect &cropRect, int levelOfDetail, KisProjectionUpdateFlags flags)
150{
152 !flags.testFlag(KisProjectionUpdateFlag::NoFilthy) ?
155
156 addJob(node, rects, cropRect, levelOfDetail,
157 type,
159}
160
161void KisSimpleUpdateQueue::addFullRefreshJob(KisNodeSP node, const QVector<QRect> &rects, const QRect &cropRect, int levelOfDetail, KisProjectionUpdateFlags flags)
162{
164 !flags.testFlag(KisProjectionUpdateFlag::NoFilthy) ?
167
168 addJob(node, rects, cropRect, levelOfDetail,
169 type,
171}
172
173void KisSimpleUpdateQueue::addUpdateJob(KisNodeSP node, const QRect &rc, const QRect& cropRect, int levelOfDetail)
174{
175 addUpdateJob(node, {rc}, cropRect, levelOfDetail, KisProjectionUpdateFlag::None);
176}
177
178void KisSimpleUpdateQueue::addFullRefreshJob(KisNodeSP node, const QRect &rc, const QRect& cropRect, int levelOfDetail)
179{
180 addFullRefreshJob(node, {rc}, cropRect, levelOfDetail, KisProjectionUpdateFlag::None);
181}
182
184 const QRect& cropRect,
185 int levelOfDetail,
187 bool dontInvalidateFrames)
188{
190
191 Q_FOREACH (const QRect &rc, rects) {
192 if (rc.isEmpty()) continue;
193
195
196 if(trySplitJob(node, rc, cropRect, levelOfDetail, type, dontInvalidateFrames)) continue;
197 if(tryMergeJob(node, rc, cropRect, levelOfDetail, type, dontInvalidateFrames)) continue;
198
199 if (type == KisBaseRectsWalker::UPDATE) {
200 KisMergeWalker::Flags flags = KisMergeWalker::DEFAULT;
201 if (dontInvalidateFrames) {
203 }
204
205 walker = new KisMergeWalker(cropRect, flags);
206 }
207 else if (type == KisBaseRectsWalker::FULL_REFRESH) {
208 KisFullRefreshWalker::Flags flags = KisFullRefreshWalker::None;
209 if (dontInvalidateFrames) {
211 }
212
213 walker = new KisFullRefreshWalker(cropRect, flags);
214 }
215 else if (type == KisBaseRectsWalker::UPDATE_NO_FILTHY) {
216 KisMergeWalker::Flags flags = KisMergeWalker::NO_FILTHY;
217 if (dontInvalidateFrames) {
219 }
220
221 walker = new KisMergeWalker(cropRect, flags);
222 }
224 KisFullRefreshWalker::Flags flags = KisFullRefreshWalker::NoFilthyMode;
225 if (dontInvalidateFrames) {
227 }
228
230 }
231 /* else if(type == KisBaseRectsWalker::UNSUPPORTED) fatalKrita; */
232
233 walker->collectRects(node, rc);
234 if (!walker->isEmpty()) {
235 walkers.append(walker);
236 }
237 }
238
239 if (!walkers.isEmpty()) {
240 m_lock.lock();
241 m_updatesList.append(walkers);
242 m_lock.unlock();
243 }
244}
245
247{
248 QMutexLocker locker(&m_lock);
249
250 KisSpontaneousJob *item;
252
253 iter.toBack();
254
255 while(iter.hasPrevious()) {
256 item = iter.previous();
257
258 if (spontaneousJob->overrides(item)) {
259 iter.remove();
260 delete item;
261 }
262 }
263
264 m_spontaneousJobsList.append(spontaneousJob);
265}
266
268{
269 QMutexLocker locker(&m_lock);
270 return m_updatesList.isEmpty() && m_spontaneousJobsList.isEmpty();
271}
272
274{
275 std::unique_lock<QMutex> locker(m_lock, std::try_to_lock);
276 if (!locker.owns_lock()) return false;
277 return m_updatesList.isEmpty() && m_spontaneousJobsList.isEmpty();
278}
279
281{
282 QMutexLocker locker(&m_lock);
283 return m_updatesList.size() + m_spontaneousJobsList.size();
284}
285
287 const QRect& cropRect,
288 int levelOfDetail,
290 bool dontInvalidateFrames)
291{
292 if(rc.width() <= m_patchWidth || rc.height() <= m_patchHeight)
293 return false;
294
295 // a bit of recursive splitting...
296
297 qint32 firstCol = rc.x() / m_patchWidth;
298 qint32 firstRow = rc.y() / m_patchHeight;
299
300 qint32 lastCol = (rc.x() + rc.width()) / m_patchWidth;
301 qint32 lastRow = (rc.y() + rc.height()) / m_patchHeight;
302
303 QVector<QRect> splitRects;
304
305 for(qint32 i = firstRow; i <= lastRow; i++) {
306 for(qint32 j = firstCol; j <= lastCol; j++) {
307 QRect maxPatchRect(j * m_patchWidth, i * m_patchHeight,
309 QRect patchRect = rc & maxPatchRect;
310 splitRects.append(patchRect);
311 }
312 }
313
314 KIS_SAFE_ASSERT_RECOVER_NOOP(!splitRects.isEmpty());
315 addJob(node, splitRects, cropRect, levelOfDetail, type, dontInvalidateFrames);
316
317 return true;
318}
319
321 const QRect& cropRect,
322 int levelOfDetail,
324 bool dontInvalidateFrames)
325{
326 QMutexLocker locker(&m_lock);
327
328 QRect baseRect = rc;
329
330 KisBaseRectsWalkerSP goodCandidate;
333
339 iter.toBack();
340
341 while(iter.hasPrevious()) {
342 item = iter.previous();
343
344 if(item->startNode() != node) continue;
345 if(item->type() != type) continue;
346 if(item->clonesDontInvalidateFrames() != dontInvalidateFrames) continue;
347 if(item->cropRect() != cropRect) continue;
348 if(item->levelOfDetail() != levelOfDetail) continue;
349
350 if(joinRects(baseRect, item->requestedRect(), m_maxMergeAlpha)) {
351 goodCandidate = item;
352 break;
353 }
354 }
355
356 if(goodCandidate)
357 collectJobs(goodCandidate, baseRect, m_maxMergeCollectAlpha);
358
359 return (bool)goodCandidate;
360}
361
363{
364 QMutexLocker locker(&m_lock);
365
366 if(m_updatesList.size() <= 1) return;
367
368 KisBaseRectsWalkerSP baseWalker = m_updatesList.first();
369 QRect baseRect = baseWalker->requestedRect();
370
371 collectJobs(baseWalker, baseRect, m_maxCollectAlpha);
372}
373
375 QRect baseRect,
376 const qreal maxAlpha)
377{
380
381 while(iter.hasNext()) {
382 item = iter.next();
383
384 if(item == baseWalker) continue;
385 if(item->type() != baseWalker->type()) continue;
386 if(item->startNode() != baseWalker->startNode()) continue;
387 if(item->clonesDontInvalidateFrames() != baseWalker->clonesDontInvalidateFrames()) continue;
388 if(item->cropRect() != baseWalker->cropRect()) continue;
389 if(item->levelOfDetail() != baseWalker->levelOfDetail()) continue;
390
391 if(joinRects(baseRect, item->requestedRect(), maxAlpha)) {
392 iter.remove();
393 }
394 }
395
396 if(baseWalker->requestedRect() != baseRect) {
397 baseWalker->collectRects(baseWalker->startNode(), baseRect);
398 }
399}
400
402 const QRect& newRect, qreal maxAlpha)
403{
404 QRect unitedRect = baseRect | newRect;
405 if(unitedRect.width() > m_patchWidth || unitedRect.height() > m_patchHeight)
406 return false;
407
408 bool result = false;
409 qint64 baseWork = qint64(baseRect.width()) * baseRect.height() +
410 qint64(newRect.width()) * newRect.height();
411
412 qint64 newWork = qint64(unitedRect.width()) * unitedRect.height();
413
414 qreal alpha = qreal(newWork) / baseWork;
415
416 if(alpha < maxAlpha) {
417 DEBUG_JOIN(baseRect, newRect, alpha);
418
420 ACCUMULATOR_ADD(baseWork, newWork);
422
423 baseRect = unitedRect;
424 result = true;
425 }
426
427 return result;
428}
429
434
void collectRects(KisNodeSP node, const QRect &requestedRect)
bool clonesDontInvalidateFrames() const
void recalculate(const QRect &requestedRect)
virtual UpdateType type() const =0
KisNodeSP startNode() const
int updatePatchWidth() const
qreal maxMergeCollectAlpha() const
int updatePatchHeight() const
qreal maxMergeAlpha() const
qreal maxCollectAlpha() const
void addJob(KisNodeSP node, const QVector< QRect > &rects, const QRect &cropRect, int levelOfDetail, KisBaseRectsWalker::UpdateType type, bool dontInvalidateFrames)
bool processOneJob(KisUpdaterContext &updaterContext)
bool joinRects(QRect &baseRect, const QRect &newRect, qreal maxAlpha)
void addFullRefreshJob(KisNodeSP node, const QVector< QRect > &rects, const QRect &cropRect, int levelOfDetail, KisProjectionUpdateFlags flags)
void collectJobs(KisBaseRectsWalkerSP &baseWalker, QRect baseRect, const qreal maxAlpha)
void addSpontaneousJob(KisSpontaneousJob *spontaneousJob)
KisSpontaneousJobsList m_spontaneousJobsList
bool tryMergeJob(KisNodeSP node, const QRect &rc, const QRect &cropRect, int levelOfDetail, KisBaseRectsWalker::UpdateType type, bool dontInvalidateFrames)
void processQueue(KisUpdaterContext &updaterContext)
void addUpdateJob(KisNodeSP node, const QVector< QRect > &rects, const QRect &cropRect, int levelOfDetail, KisProjectionUpdateFlags flags)
bool trySplitJob(KisNodeSP node, const QRect &rc, const QRect &cropRect, int levelOfDetail, KisBaseRectsWalker::UpdateType type, bool dontInvalidateFrames)
virtual bool overrides(const KisSpontaneousJob *otherJob)=0
KisSpontaneousJobsList & getSpontaneousJobsList()
void getJobsSnapshot(qint32 &numMergeJobs, qint32 &numStrokeJobs)
bool isJobAllowed(KisBaseRectsWalkerSP walker)
void addMergeJob(KisBaseRectsWalkerSP walker)
void addSpontaneousJob(KisSpontaneousJob *spontaneousJob)
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
#define DECLARE_ACCUMULATOR()
#define ACCUMULATOR_ADD(baseAmount, newAmount)
#define DEBUG_JOIN(baseRect, newRect, alpha)
#define ACCUMULATOR_DEBUG()
QListIterator< KisBaseRectsWalkerSP > KisWalkersListIterator
QMutableListIterator< KisSpontaneousJob * > KisMutableSpontaneousJobsListIterator
QMutableListIterator< KisBaseRectsWalkerSP > KisMutableWalkersListIterator