Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_strokes_queue.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2011 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7#include "kis_strokes_queue.h"
8
9#include <QQueue>
10#include <QMutex>
11#include <QMutexLocker>
12#include "kis_stroke.h"
13#include "kis_updater_context.h"
15#include "kis_stroke_strategy.h"
16#include "kis_undo_stores.h"
18#include "KisCppQuirks.h"
19
20typedef QQueue<KisStrokeSP> StrokesQueue;
21typedef QQueue<KisStrokeSP>::iterator StrokesQueueIterator;
22
25{
26public:
28
29 KisStrokeId startStroke(KisStrokeStrategy *strokeStrategy) override {
30 return q->startLodNUndoStroke(strokeStrategy);
31 }
32
33 void addJob(KisStrokeId id, KisStrokeJobData *data) override {
34 KisStrokeSP stroke = id.toStrongRef();
36 KIS_SAFE_ASSERT_RECOVER_NOOP(!stroke->lodBuddy());
38
39 q->addJob(id, data);
40 }
41
42 void endStroke(KisStrokeId id) override {
43 KisStrokeSP stroke = id.toStrongRef();
45 KIS_SAFE_ASSERT_RECOVER_NOOP(!stroke->lodBuddy());
47
48 q->endStroke(id);
49 }
50
51 bool cancelStroke(KisStrokeId id) override {
52 Q_UNUSED(id);
53 qFatal("Not implemented");
54 return false;
55 }
56
57private:
59};
60
61
62struct Q_DECL_HIDDEN KisStrokesQueue::Private {
64 : q(_q),
65 openedStrokesCounter(0),
66 needsExclusiveAccess(false),
67 wrapAroundModeSupported(false),
68 balancingRatioOverride(-1.0),
69 currentStrokeLoaded(false),
70 lodNNeedsSynchronization(true),
71 desiredLevelOfDetail(0),
72 nextDesiredLevelOfDetail(0),
73 lodNStrokesFacade(_q),
74 lodNPostExecutionUndoAdapter(&lodNUndoStore, &lodNStrokesFacade) {}
75
83
87 QMutex mutex;
90 std::function<void()> purgeRedoStateCallback;
96
98 void startLod0ToNStroke(int levelOfDetail, bool forgettable);
99
100
101 std::pair<StrokesQueueIterator, StrokesQueueIterator> currentLodRange();
105
106 void switchDesiredLevelOfDetail(bool forced);
111};
112
113
115 : m_d(new Private(this))
116{
117}
118
120{
121 Q_FOREACH (KisStrokeSP stroke, m_d->strokesQueue) {
122 stroke->cancelStroke();
123 }
124
125 delete m_d;
126}
127
128template <class StrokePair, class StrokesQueue>
129typename StrokesQueue::iterator
130executeStrokePair(const StrokePair &pair, StrokesQueue &queue, typename StrokesQueue::iterator it, KisStroke::Type type, int levelOfDetail, KisStrokesQueueMutatedJobInterface *mutatedJobsInterface) {
131 KisStrokeStrategy *strategy = pair.first;
132 QList<KisStrokeJobData*> jobsData = pair.second;
133
134 KisStrokeSP stroke(new KisStroke(strategy, type, levelOfDetail));
135 strategy->setMutatedJobsInterface(mutatedJobsInterface, stroke);
136 it = queue.insert(it, stroke);
137 Q_FOREACH (KisStrokeJobData *jobData, jobsData) {
138 stroke->addJob(jobData);
139 }
140 stroke->endStroke();
141
142 return it;
143}
144
145void KisStrokesQueue::Private::startLod0ToNStroke(int levelOfDetail, bool forgettable)
146{
147 // precondition: lock held!
148 // precondition: lod > 0
149 KIS_ASSERT_RECOVER_RETURN(levelOfDetail);
150
151 {
152 // sanity check: there should be no open LoD range now!
155 std::tie(it, end) = currentLodRange();
157 }
158
159 if (!this->lod0ToNStrokeStrategyFactory) return;
160
161 KisLodSyncPair syncPair = this->lod0ToNStrokeStrategyFactory(forgettable);
162 executeStrokePair(syncPair, this->strokesQueue, this->strokesQueue.end(), KisStroke::LODN, levelOfDetail, q);
163
164 this->lodNNeedsSynchronization = false;
165}
166
167void KisStrokesQueue::Private::cancelForgettableStrokes()
168{
169 if (!strokesQueue.isEmpty() && !hasUnfinishedStrokes()) {
170 Q_FOREACH (KisStrokeSP stroke, strokesQueue) {
171 KIS_ASSERT_RECOVER_NOOP(stroke->isEnded());
172
173 if (stroke->canForgetAboutMe()) {
174 stroke->cancelStroke();
175 }
176 }
177 }
178}
179
180std::pair<StrokesQueueIterator, StrokesQueueIterator> KisStrokesQueue::Private::currentLodRange()
181{
187 for (auto it = std::make_reverse_iterator(strokesQueue.end());
188 it != std::make_reverse_iterator(strokesQueue.begin());
189 ++it) {
190
191 if ((*it)->type() == KisStroke::LEGACY) {
192 // it.base() returns the next element after the one we found
193 return std::make_pair(it.base(), strokesQueue.end());
194 }
195 }
196
197 return std::make_pair(strokesQueue.begin(), strokesQueue.end());
198}
199
200bool KisStrokesQueue::Private::shouldWrapInSuspendUpdatesStroke()
201{
204 std::tie(it, end) = currentLodRange();
205
206 for (; it != end; ++it) {
207 KisStrokeSP stroke = *it;
208
209 if (stroke->isCancelled()) continue;
210
211 if (stroke->type() == KisStroke::RESUME) {
212 return false;
213 }
214 }
215
216 return true;
217}
218
219StrokesQueueIterator KisStrokesQueue::Private::findNewLod0Pos()
220{
223 std::tie(it, end) = currentLodRange();
224
225 for (; it != end; ++it) {
226 if ((*it)->isCancelled()) continue;
227
228 if ((*it)->type() == KisStroke::RESUME) {
229 return it;
230 }
231 }
232
233 return it;
234}
235
236StrokesQueueIterator KisStrokesQueue::Private::findNewLodNPos(KisStrokeSP lodN)
237{
240 std::tie(it, end) = currentLodRange();
241
242 for (; it != end; ++it) {
243 if ((*it)->isCancelled()) continue;
244
245 if ((*it)->type() == KisStroke::LOD0 ||
246 (*it)->type() == KisStroke::SUSPEND ||
247 (*it)->type() == KisStroke::RESUME) {
248
249 if (it != end && it == strokesQueue.begin()) {
250 KisStrokeSP head = *it;
251
252 if (head->supportsSuspension()) {
253 head->suspendStroke(lodN);
254 }
255 }
256
257 return it;
258 }
259 }
260
261 return it;
262}
263
265{
266 QMutexLocker locker(&m_d->mutex);
267
268 KIS_SAFE_ASSERT_RECOVER_NOOP(!m_d->lodNNeedsSynchronization);
269 KIS_SAFE_ASSERT_RECOVER_NOOP(m_d->desiredLevelOfDetail > 0);
270
271 KisStrokeSP buddy(new KisStroke(strokeStrategy, KisStroke::LODN, m_d->desiredLevelOfDetail));
272 strokeStrategy->setMutatedJobsInterface(this, buddy);
273 m_d->strokesQueue.insert(m_d->findNewLodNPos(buddy), buddy);
274
275 KisStrokeId id(buddy);
276 m_d->openedStrokesCounter++;
277
278 return id;
279}
280
282{
283 QMutexLocker locker(&m_d->mutex);
284
285 KisStrokeSP stroke;
286 KisStrokeStrategy* lodBuddyStrategy;
287
288 // we should let forgettable strokes to queue up
289 if (!strokeStrategy->canForgetAboutMe()) {
290 m_d->cancelForgettableStrokes();
291 }
292
293 if (m_d->desiredLevelOfDetail &&
294 (m_d->lodPreferences.lodPreferred() || strokeStrategy->forceLodModeIfPossible()) &&
295 (lodBuddyStrategy =
296 strokeStrategy->createLodClone(m_d->desiredLevelOfDetail))) {
297
298 if (m_d->lodNNeedsSynchronization) {
299 m_d->startLod0ToNStroke(m_d->desiredLevelOfDetail, false);
300 }
301
302 stroke = KisStrokeSP(new KisStroke(strokeStrategy, KisStroke::LOD0, 0));
303
304 KisStrokeSP buddy(new KisStroke(lodBuddyStrategy, KisStroke::LODN, m_d->desiredLevelOfDetail));
305 lodBuddyStrategy->setMutatedJobsInterface(this, buddy);
306 stroke->setLodBuddy(buddy);
307 m_d->strokesQueue.insert(m_d->findNewLodNPos(buddy), buddy);
308
309 if (m_d->shouldWrapInSuspendUpdatesStroke()) {
310
311 KisSuspendResumePair suspendPair;
312 KisSuspendResumePair resumePair;
313 std::tie(suspendPair, resumePair) = m_d->suspendResumeUpdatesStrokeStrategyFactory();
314
315 StrokesQueueIterator it = m_d->findNewLod0Pos();
316
317 it = executeStrokePair(resumePair, m_d->strokesQueue, it, KisStroke::RESUME, 0, this);
318 it = m_d->strokesQueue.insert(it, stroke);
319 it = executeStrokePair(suspendPair, m_d->strokesQueue, it, KisStroke::SUSPEND, 0, this);
320
321 } else {
322 m_d->strokesQueue.insert(m_d->findNewLod0Pos(), stroke);
323 }
324
325 } else {
326 stroke = KisStrokeSP(new KisStroke(strokeStrategy, KisStroke::LEGACY, 0));
327 m_d->strokesQueue.enqueue(stroke);
328 }
329
330 KisStrokeId id(stroke);
331 strokeStrategy->setMutatedJobsInterface(this, id);
332
333 m_d->openedStrokesCounter++;
334
335 if (stroke->type() == KisStroke::LEGACY) {
336 m_d->lodNNeedsSynchronization = true;
337 }
338
339 return id;
340}
341
343{
344 QMutexLocker locker(&m_d->mutex);
345
346 KisStrokeSP stroke = id.toStrongRef();
348
349 KisStrokeSP buddy = stroke->lodBuddy();
350 if (buddy) {
351 KisStrokeJobData *clonedData =
352 data->createLodClone(buddy->worksOnLevelOfDetail());
353 KIS_ASSERT_RECOVER_RETURN(clonedData);
354
355 buddy->addJob(clonedData);
356 }
357
358 stroke->addJob(data);
359}
360
362{
363 QMutexLocker locker(&m_d->mutex);
364
365 KisStrokeSP stroke = id.toStrongRef();
367
368 stroke->addMutatedJobs(list);
369}
370
372{
373 QMutexLocker locker(&m_d->mutex);
374
375 KisStrokeSP stroke = id.toStrongRef();
377 stroke->endStroke();
378 m_d->openedStrokesCounter--;
379
380 KisStrokeSP buddy = stroke->lodBuddy();
381 if (buddy) {
382 buddy->endStroke();
383 }
384}
385
387{
388 QMutexLocker locker(&m_d->mutex);
389
390 KisStrokeSP stroke = id.toStrongRef();
391 if(stroke) {
392 stroke->cancelStroke();
393 m_d->openedStrokesCounter--;
394
395 KisStrokeSP buddy = stroke->lodBuddy();
396 if (buddy) {
397 buddy->cancelStroke();
398 }
399 }
400 return bool(stroke);
401}
402
403bool KisStrokesQueue::Private::hasUnfinishedStrokes() const
404{
405 Q_FOREACH (KisStrokeSP stroke, strokesQueue) {
406 if (!stroke->isEnded()) {
407 return true;
408 }
409 }
410
411 return false;
412}
413
415{
416 bool anythingCanceled = false;
417
418 QMutexLocker locker(&m_d->mutex);
419
425 if (!m_d->strokesQueue.isEmpty() &&
426 !m_d->hasUnfinishedStrokes()) {
427
436 const auto lastNonCancellableStrokeIt =
437 std::find_if_not(std::make_reverse_iterator(m_d->strokesQueue.end()),
438 std::make_reverse_iterator(m_d->strokesQueue.begin()),
440
441 bool needsLodNSynchronization = false;
442
443 for (auto it = lastNonCancellableStrokeIt.base(); it != m_d->strokesQueue.end(); it++) {
444 KisStrokeSP currentStroke = *it;
445 KIS_ASSERT_RECOVER_NOOP(currentStroke->isEnded());
446 KIS_ASSERT_RECOVER_NOOP(currentStroke->isAsynchronouslyCancellable());
447
448 currentStroke->cancelStroke();
449 anythingCanceled = true;
450
451 // we shouldn't cancel buddies...
452 if (currentStroke->type() == KisStroke::LOD0) {
458 needsLodNSynchronization = true;
459 }
460
461 }
462
463 if (needsLodNSynchronization) {
464 m_d->forceResetLodAndCloseCurrentLodRange();
465 }
466 }
467
473 return anythingCanceled;
474}
475
477{
478 UndoResult result = UNDO_FAIL;
479
480 QMutexLocker locker(&m_d->mutex);
481
482 KisStrokeSP lastStroke;
483 KisStrokeSP lastBuddy;
484 bool buddyFound = false;
485
486 for (auto it = std::make_reverse_iterator(m_d->strokesQueue.constEnd());
487 it != std::make_reverse_iterator(m_d->strokesQueue.constBegin());
488 ++it) {
489
490 if ((*it)->type() == KisStroke::LEGACY) {
491 break;
492 }
493
494 if (!lastStroke && (*it)->type() == KisStroke::LOD0 && !(*it)->isCancelled()) {
495 lastStroke = *it;
496 lastBuddy = lastStroke->lodBuddy();
497
498 KIS_SAFE_ASSERT_RECOVER(lastBuddy) {
499 lastStroke.clear();
500 lastBuddy.clear();
501 break;
502 }
503 }
504
505 KIS_SAFE_ASSERT_RECOVER(!lastStroke || *it == lastBuddy || (*it)->type() != KisStroke::LODN) {
506 lastStroke.clear();
507 lastBuddy.clear();
508 break;
509 }
510
511 if (lastStroke && *it == lastBuddy) {
512 KIS_SAFE_ASSERT_RECOVER(lastBuddy->type() == KisStroke::LODN) {
513 lastStroke.clear();
514 lastBuddy.clear();
515 break;
516 }
517 buddyFound = true;
518 break;
519 }
520 }
521
522 if (!lastStroke) return UNDO_FAIL;
523 if (!lastStroke->isEnded()) return UNDO_FAIL;
524 if (lastStroke->isCancelled()) return UNDO_FAIL;
525
526 KIS_SAFE_ASSERT_RECOVER_NOOP(!buddyFound ||
527 lastStroke->isCancelled() == lastBuddy->isCancelled());
528 KIS_SAFE_ASSERT_RECOVER_NOOP(lastBuddy->isEnded());
529
530 if (!lastStroke->canCancel()) {
531 return UNDO_WAIT;
532 }
533 lastStroke->cancelStroke();
534
535 if (buddyFound && lastBuddy->canCancel()) {
536 lastBuddy->cancelStroke();
537 } else {
538 // TODO: assert that checks that there is no other lodn strokes
539 locker.unlock();
540 m_d->lodNUndoStore.undo();
541 m_d->lodNUndoStore.purgeRedoState();
542 locker.relock();
543 }
544
545 result = UNDO_OK;
546
547 return result;
548}
549
550void KisStrokesQueue::Private::tryClearUndoOnStrokeCompletion(KisStrokeSP finishingStroke)
551{
552 if (finishingStroke->type() != KisStroke::RESUME) return;
553
554 bool hasResumeStrokes = false;
555 bool hasLod0Strokes = false;
556
557 auto it = std::find(strokesQueue.begin(), strokesQueue.end(), finishingStroke);
558 KIS_SAFE_ASSERT_RECOVER_RETURN(it != strokesQueue.end());
559 ++it;
560
561 for (; it != strokesQueue.end(); ++it) {
562 KisStrokeSP stroke = *it;
563 if (stroke->type() == KisStroke::LEGACY) break;
564
565 hasLod0Strokes |= stroke->type() == KisStroke::LOD0;
566 hasResumeStrokes |= stroke->type() == KisStroke::RESUME;
567 }
568
569 KIS_SAFE_ASSERT_RECOVER_NOOP(!hasLod0Strokes || hasResumeStrokes);
570
571 if (!hasResumeStrokes && !hasLod0Strokes) {
572 lodNUndoStore.clear();
573 }
574}
575
576void KisStrokesQueue::Private::forceResetLodAndCloseCurrentLodRange()
577{
578 lodNNeedsSynchronization = true;
579
580 if (!strokesQueue.isEmpty() && strokesQueue.last()->type() != KisStroke::LEGACY) {
581
582 std::pair<KisStrokeStrategy*, QList<KisStrokeJobData*>> fakeStrokePair
583 (new KisStrokeStrategy(QLatin1String("fake_sync")), {});
584
585 executeStrokePair(fakeStrokePair, this->strokesQueue, this->strokesQueue.end(), KisStroke::LEGACY, 0, q);
586 }
587}
588
590 bool externalJobsPending)
591{
592 updaterContext.lock();
593 m_d->mutex.lock();
594
595 while(updaterContext.hasSpareThread() &&
596 processOneJob(updaterContext,
597 externalJobsPending));
598
599 m_d->mutex.unlock();
600 updaterContext.unlock();
601}
602
604{
605 return m_d->needsExclusiveAccess;
606}
607
609{
610 return m_d->wrapAroundModeSupported;
611}
612
614{
615 return m_d->balancingRatioOverride;
616}
617
619{
620 QMutexLocker locker(&m_d->mutex);
621
626 return KisLodPreferences(m_d->lodPreferences.flags(), m_d->desiredLevelOfDetail);
627}
628
630{
631 QMutexLocker locker(&m_d->mutex);
632
633 m_d->lodPreferences = value;
634
635 if (m_d->lodPreferences.desiredLevelOfDetail() != m_d->nextDesiredLevelOfDetail ||
636 (m_d->lodPreferences.lodPreferred() && m_d->lodNNeedsSynchronization)) {
637
638 m_d->nextDesiredLevelOfDetail = m_d->lodPreferences.desiredLevelOfDetail();
639 m_d->switchDesiredLevelOfDetail(false);
640 }
641}
642
644{
645 QMutexLocker locker(&m_d->mutex);
646 return m_d->strokesQueue.isEmpty();
647}
648
650{
651 std::unique_lock<QMutex> locker(m_d->mutex, std::try_to_lock);
652 if (!locker.owns_lock()) return false;
653 return m_d->strokesQueue.isEmpty();
654}
655
657{
658 QMutexLocker locker(&m_d->mutex);
659 if(m_d->strokesQueue.isEmpty()) return 0;
660
661 // just a rough approximation
662 return qMax(1, m_d->strokesQueue.head()->numJobs()) * m_d->strokesQueue.size();
663}
664
665void KisStrokesQueue::Private::switchDesiredLevelOfDetail(bool forced)
666{
667 if (forced || nextDesiredLevelOfDetail != desiredLevelOfDetail) {
668 Q_FOREACH (KisStrokeSP stroke, strokesQueue) {
669 if (stroke->type() != KisStroke::LEGACY)
670 return;
671 }
672
673 const bool forgettable =
674 forced && !lodNNeedsSynchronization &&
675 desiredLevelOfDetail == nextDesiredLevelOfDetail;
676
677 desiredLevelOfDetail = nextDesiredLevelOfDetail;
678 lodNNeedsSynchronization |= !forgettable;
679
680 if (desiredLevelOfDetail && lodPreferences.lodPreferred()) {
681 startLod0ToNStroke(desiredLevelOfDetail, forgettable);
682 }
683 }
684}
685
687{
688 QMutexLocker locker(&m_d->mutex);
689 m_d->switchDesiredLevelOfDetail(true);
690}
691
693{
694 QMutexLocker locker(&m_d->mutex);
695
696 m_d->forceResetLodAndCloseCurrentLodRange();
697}
698
700{
701 QMutexLocker locker(&m_d->mutex);
702
703 qDebug() <<"===";
704 Q_FOREACH (KisStrokeSP stroke, m_d->strokesQueue) {
705 qDebug() << ppVar(stroke->name()) << ppVar(stroke->type()) << ppVar(stroke->numJobs()) << ppVar(stroke->isInitialized()) << ppVar(stroke->isCancelled());
706 }
707 qDebug() <<"===";
708}
709
711{
712 m_d->lod0ToNStrokeStrategyFactory = factory;
713}
714
716{
717 m_d->suspendResumeUpdatesStrokeStrategyFactory = factory;
718}
719
720void KisStrokesQueue::setPurgeRedoStateCallback(const std::function<void ()> &callback)
721{
722 m_d->purgeRedoStateCallback = callback;
723}
724
726{
727 m_d->postSyncLod0GUIPlaneRequestForResume = callback;
728}
729
731{
732 return &m_d->lodNPostExecutionUndoAdapter;
733}
734
736{
737 QMutexLocker locker(&m_d->mutex);
738 if(m_d->strokesQueue.isEmpty()) return KUndo2MagicString();
739
740 return m_d->strokesQueue.head()->name();
741}
742
744{
745 QMutexLocker locker(&m_d->mutex);
746 return m_d->openedStrokesCounter;
747}
748
750 bool externalJobsPending)
751{
752 if(m_d->strokesQueue.isEmpty()) return false;
753 bool result = false;
754
755 const int levelOfDetail = updaterContext.currentLevelOfDetail();
756
757 const KisUpdaterContextSnapshotEx snapshot = updaterContext.getContextSnapshotEx();
758
759 const bool hasStrokeJobs = !(snapshot == ContextEmpty ||
760 snapshot == HasMergeJob);
761 const bool hasMergeJobs = snapshot & HasMergeJob;
762
763 if(checkStrokeState(hasStrokeJobs, levelOfDetail) &&
764 checkExclusiveProperty(hasMergeJobs, hasStrokeJobs) &&
765 checkSequentialProperty(snapshot, externalJobsPending)) {
766
767 KisStrokeSP stroke = m_d->strokesQueue.head();
768 updaterContext.addStrokeJob(stroke->popOneJob());
769 result = true;
770 }
771
772 return result;
773}
774
775void KisStrokesQueue::Private::loadStroke(KisStrokeSP stroke)
776{
777 needsExclusiveAccess = stroke->isExclusive();
778 wrapAroundModeSupported = stroke->supportsWrapAroundMode();
779 balancingRatioOverride = stroke->balancingRatioOverride();
780 currentStrokeLoaded = true;
781
788 if (purgeRedoStateCallback &&
789 stroke->clearsRedoOnStart()) {
790
791 purgeRedoStateCallback();
792 }
793}
794
795bool KisStrokesQueue::checkStrokeState(bool hasStrokeJobsRunning,
796 int runningLevelOfDetail)
797{
798 KisStrokeSP stroke = m_d->strokesQueue.head();
799 bool result = false;
800
805 bool hasLodCompatibility = checkLevelOfDetailProperty(runningLevelOfDetail);
806 bool hasJobs = stroke->hasJobs();
807
817 if(!stroke->isInitialized() && hasJobs && hasLodCompatibility) {
823 if (!m_d->currentStrokeLoaded) {
824 m_d->loadStroke(stroke);
825 }
826
827 result = true;
828 }
829 else if(hasJobs && hasLodCompatibility) {
834 if (!m_d->currentStrokeLoaded) {
835 m_d->loadStroke(stroke);
836 }
837
838 result = true;
839 }
840 else if(stroke->isEnded() && !hasJobs && !hasStrokeJobsRunning) {
841 m_d->tryClearUndoOnStrokeCompletion(stroke);
842
843 const bool needsSyncLod0PlaneToGUI =
844 stroke->type() == KisStroke::LOD0 &&
845 stroke->isCancelled();
846
853 if (needsSyncLod0PlaneToGUI &&
854 m_d->postSyncLod0GUIPlaneRequestForResume) {
855 m_d->postSyncLod0GUIPlaneRequestForResume();
856 }
857
858 m_d->strokesQueue.dequeue(); // deleted by shared pointer
859 m_d->needsExclusiveAccess = false;
860 m_d->wrapAroundModeSupported = false;
861 m_d->balancingRatioOverride = -1.0;
862 m_d->currentStrokeLoaded = false;
863
864 m_d->switchDesiredLevelOfDetail(false);
865
866 if(!m_d->strokesQueue.isEmpty()) {
867 result = checkStrokeState(false, runningLevelOfDetail);
868 }
869 }
870
871 return result;
872}
873
875 bool hasStrokeJobs)
876{
877 Q_UNUSED(hasStrokeJobs);
878
879 if(!m_d->strokesQueue.head()->isExclusive()) return true;
880 return hasMergeJobs == 0;
881}
882
883bool KisStrokesQueue::checkSequentialProperty(KisUpdaterContextSnapshotEx snapshot,
884 bool externalJobsPending)
885{
886 KisStrokeSP stroke = m_d->strokesQueue.head();
887
888 if (snapshot & HasSequentialJob ||
889 snapshot & HasBarrierJob) {
890 return false;
891 }
892
893 KisStrokeJobData::Sequentiality nextSequentiality =
894 stroke->nextJobSequentiality();
895
896 if (nextSequentiality == KisStrokeJobData::UNIQUELY_CONCURRENT &&
897 snapshot & HasUniquelyConcurrentJob) {
898
899 return false;
900 }
901
902 if (nextSequentiality == KisStrokeJobData::SEQUENTIAL &&
903 (snapshot & HasUniquelyConcurrentJob ||
904 snapshot & HasConcurrentJob)) {
905
906 return false;
907 }
908
909 if (nextSequentiality == KisStrokeJobData::BARRIER &&
910 (snapshot & HasUniquelyConcurrentJob ||
911 snapshot & HasConcurrentJob ||
912 snapshot & HasMergeJob ||
913 externalJobsPending)) {
914
915 return false;
916 }
917
918 return true;
919}
920
922{
923 KisStrokeSP stroke = m_d->strokesQueue.head();
924
925 return runningLevelOfDetail < 0 ||
926 stroke->nextJobLevelOfDetail() == runningLevelOfDetail;
927}
float value(const T *src, size_t ch)
virtual KisStrokeJobData * createLodClone(int levelOfDetail)
bool forceLodModeIfPossible() const
void setMutatedJobsInterface(KisStrokesQueueMutatedJobInterface *mutatedJobsInterface, KisStrokeId strokeId)
virtual KisStrokeStrategy * createLodClone(int levelOfDetail)
bool isAsynchronouslyCancellable() const
void endStroke(KisStrokeId id) override
bool cancelStroke(KisStrokeId id) override
KisStrokeId startStroke(KisStrokeStrategy *strokeStrategy) override
void addJob(KisStrokeId id, KisStrokeJobData *data) override
KisUpdaterContextSnapshotEx getContextSnapshotEx() const
void addStrokeJob(KisStrokeJob *strokeJob)
#define KIS_SAFE_ASSERT_RECOVER(cond)
Definition kis_assert.h:126
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define KIS_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:75
#define KIS_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:97
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
#define ppVar(var)
Definition kis_debug.h:155
typedef void(QOPENGLF_APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC)(GLuint buffer)
std::pair< KisStrokeStrategy *, QList< KisStrokeJobData * > > KisSuspendResumePair
std::function< std::pair< KisSuspendResumePair, KisSuspendResumePair >()> KisSuspendResumeStrategyPairFactory
std::pair< KisStrokeStrategy *, QList< KisStrokeJobData * > > KisLodSyncPair
std::function< KisLodSyncPair(bool)> KisLodSyncStrokeStrategyFactory
QQueue< KisStrokeSP >::iterator StrokesQueueIterator
QQueue< KisStrokeSP > StrokesQueue
StrokesQueue::iterator executeStrokePair(const StrokePair &pair, StrokesQueue &queue, typename StrokesQueue::iterator it, KisStroke::Type type, int levelOfDetail, KisStrokesQueueMutatedJobInterface *mutatedJobsInterface)
QSharedPointer< KisStroke > KisStrokeSP
Definition kis_types.h:270
UndoResult tryUndoLastStrokeAsync()
bool shouldWrapInSuspendUpdatesStroke()
void startLod0ToNStroke(int levelOfDetail, bool forgettable)
qint32 sizeMetric() const
StrokesQueue strokesQueue
void forceResetLodAndCloseCurrentLodRange()
void switchDesiredLevelOfDetail(bool forced)
bool hasUnfinishedStrokes() const
void loadStroke(KisStrokeSP stroke)
void endStroke(KisStrokeId id)
bool checkExclusiveProperty(bool hasMergeJobs, bool hasStrokeJobs)
void cancelForgettableStrokes()
KisSurrogateUndoStore lodNUndoStore
Private(KisStrokesQueue *_q)
void addJob(KisStrokeId id, KisStrokeJobData *data)
KisStrokesQueue * q
void setPurgeRedoStateCallback(const std::function< void()> &callback)
void setLod0ToNStrokeStrategyFactory(const KisLodSyncStrokeStrategyFactory &factory)
void addMutatedJobs(KisStrokeId id, const QVector< KisStrokeJobData * > list) final override
bool checkStrokeState(bool hasStrokeJobsRunning, int runningLevelOfDetail)
bool processOneJob(KisUpdaterContext &updaterContext, bool externalJobsPending)
bool checkLevelOfDetailProperty(int runningLevelOfDetail)
void setPostSyncLod0GUIPlaneRequestForResumeCallback(const std::function< void()> &callback)
StrokesQueueIterator findNewLod0Pos()
void setSuspendResumeUpdatesStrokeStrategyFactory(const KisSuspendResumeStrategyPairFactory &factory)
KisPostExecutionUndoAdapter lodNPostExecutionUndoAdapter
bool checkSequentialProperty(KisUpdaterContextSnapshotEx snapshot, bool externalJobsPending)
LodNUndoStrokesFacade lodNStrokesFacade
bool cancelStroke(KisStrokeId id)
void processQueue(KisUpdaterContext &updaterContext, bool externalJobsPending)
Private *const m_d
KisStrokeId startStroke(KisStrokeStrategy *strokeStrategy)
void tryClearUndoOnStrokeCompletion(KisStrokeSP finishingStroke)
bool hasOpenedStrokes() const
KUndo2MagicString currentStrokeName() const
StrokesQueueIterator findNewLodNPos(KisStrokeSP lodN)
std::function< void()> postSyncLod0GUIPlaneRequestForResume
KisLodPreferences lodPreferences
KisSuspendResumeStrategyPairFactory suspendResumeUpdatesStrokeStrategyFactory
KisLodSyncStrokeStrategyFactory lod0ToNStrokeStrategyFactory
void explicitRegenerateLevelOfDetail()
std::pair< StrokesQueueIterator, StrokesQueueIterator > currentLodRange()
std::function< void()> purgeRedoStateCallback
KisStrokeId startLodNUndoStroke(KisStrokeStrategy *strokeStrategy)
void setLodPreferences(const KisLodPreferences &value)