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 walkers.append(walker);
235 }
236
237 if (!walkers.isEmpty()) {
238 m_lock.lock();
239 m_updatesList.append(walkers);
240 m_lock.unlock();
241 }
242}
243
245{
246 QMutexLocker locker(&m_lock);
247
248 KisSpontaneousJob *item;
250
251 iter.toBack();
252
253 while(iter.hasPrevious()) {
254 item = iter.previous();
255
256 if (spontaneousJob->overrides(item)) {
257 iter.remove();
258 delete item;
259 }
260 }
261
262 m_spontaneousJobsList.append(spontaneousJob);
263}
264
266{
267 QMutexLocker locker(&m_lock);
268 return m_updatesList.isEmpty() && m_spontaneousJobsList.isEmpty();
269}
270
272{
273 QMutexLocker locker(&m_lock);
274 return m_updatesList.size() + m_spontaneousJobsList.size();
275}
276
278 const QRect& cropRect,
279 int levelOfDetail,
281 bool dontInvalidateFrames)
282{
283 if(rc.width() <= m_patchWidth || rc.height() <= m_patchHeight)
284 return false;
285
286 // a bit of recursive splitting...
287
288 qint32 firstCol = rc.x() / m_patchWidth;
289 qint32 firstRow = rc.y() / m_patchHeight;
290
291 qint32 lastCol = (rc.x() + rc.width()) / m_patchWidth;
292 qint32 lastRow = (rc.y() + rc.height()) / m_patchHeight;
293
294 QVector<QRect> splitRects;
295
296 for(qint32 i = firstRow; i <= lastRow; i++) {
297 for(qint32 j = firstCol; j <= lastCol; j++) {
298 QRect maxPatchRect(j * m_patchWidth, i * m_patchHeight,
300 QRect patchRect = rc & maxPatchRect;
301 splitRects.append(patchRect);
302 }
303 }
304
305 KIS_SAFE_ASSERT_RECOVER_NOOP(!splitRects.isEmpty());
306 addJob(node, splitRects, cropRect, levelOfDetail, type, dontInvalidateFrames);
307
308 return true;
309}
310
312 const QRect& cropRect,
313 int levelOfDetail,
315 bool dontInvalidateFrames)
316{
317 QMutexLocker locker(&m_lock);
318
319 QRect baseRect = rc;
320
321 KisBaseRectsWalkerSP goodCandidate;
324
330 iter.toBack();
331
332 while(iter.hasPrevious()) {
333 item = iter.previous();
334
335 if(item->startNode() != node) continue;
336 if(item->type() != type) continue;
337 if(item->clonesDontInvalidateFrames() != dontInvalidateFrames) continue;
338 if(item->cropRect() != cropRect) continue;
339 if(item->levelOfDetail() != levelOfDetail) continue;
340
341 if(joinRects(baseRect, item->requestedRect(), m_maxMergeAlpha)) {
342 goodCandidate = item;
343 break;
344 }
345 }
346
347 if(goodCandidate)
348 collectJobs(goodCandidate, baseRect, m_maxMergeCollectAlpha);
349
350 return (bool)goodCandidate;
351}
352
354{
355 QMutexLocker locker(&m_lock);
356
357 if(m_updatesList.size() <= 1) return;
358
359 KisBaseRectsWalkerSP baseWalker = m_updatesList.first();
360 QRect baseRect = baseWalker->requestedRect();
361
362 collectJobs(baseWalker, baseRect, m_maxCollectAlpha);
363}
364
366 QRect baseRect,
367 const qreal maxAlpha)
368{
371
372 while(iter.hasNext()) {
373 item = iter.next();
374
375 if(item == baseWalker) continue;
376 if(item->type() != baseWalker->type()) continue;
377 if(item->startNode() != baseWalker->startNode()) continue;
378 if(item->clonesDontInvalidateFrames() != baseWalker->clonesDontInvalidateFrames()) continue;
379 if(item->cropRect() != baseWalker->cropRect()) continue;
380 if(item->levelOfDetail() != baseWalker->levelOfDetail()) continue;
381
382 if(joinRects(baseRect, item->requestedRect(), maxAlpha)) {
383 iter.remove();
384 }
385 }
386
387 if(baseWalker->requestedRect() != baseRect) {
388 baseWalker->collectRects(baseWalker->startNode(), baseRect);
389 }
390}
391
393 const QRect& newRect, qreal maxAlpha)
394{
395 QRect unitedRect = baseRect | newRect;
396 if(unitedRect.width() > m_patchWidth || unitedRect.height() > m_patchHeight)
397 return false;
398
399 bool result = false;
400 qint64 baseWork = qint64(baseRect.width()) * baseRect.height() +
401 qint64(newRect.width()) * newRect.height();
402
403 qint64 newWork = qint64(unitedRect.width()) * unitedRect.height();
404
405 qreal alpha = qreal(newWork) / baseWork;
406
407 if(alpha < maxAlpha) {
408 DEBUG_JOIN(baseRect, newRect, alpha);
409
411 ACCUMULATOR_ADD(baseWork, newWork);
413
414 baseRect = unitedRect;
415 result = true;
416 }
417
418 return result;
419}
420
425
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