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 QMutexLocker locker(&m_d->mutex);
652 if(m_d->strokesQueue.isEmpty()) return 0;
653
654 // just a rough approximation
655 return qMax(1, m_d->strokesQueue.head()->numJobs()) * m_d->strokesQueue.size();
656}
657
658void KisStrokesQueue::Private::switchDesiredLevelOfDetail(bool forced)
659{
660 if (forced || nextDesiredLevelOfDetail != desiredLevelOfDetail) {
661 Q_FOREACH (KisStrokeSP stroke, strokesQueue) {
662 if (stroke->type() != KisStroke::LEGACY)
663 return;
664 }
665
666 const bool forgettable =
667 forced && !lodNNeedsSynchronization &&
668 desiredLevelOfDetail == nextDesiredLevelOfDetail;
669
670 desiredLevelOfDetail = nextDesiredLevelOfDetail;
671 lodNNeedsSynchronization |= !forgettable;
672
673 if (desiredLevelOfDetail && lodPreferences.lodPreferred()) {
674 startLod0ToNStroke(desiredLevelOfDetail, forgettable);
675 }
676 }
677}
678
680{
681 QMutexLocker locker(&m_d->mutex);
682 m_d->switchDesiredLevelOfDetail(true);
683}
684
686{
687 QMutexLocker locker(&m_d->mutex);
688
689 m_d->forceResetLodAndCloseCurrentLodRange();
690}
691
693{
694 QMutexLocker locker(&m_d->mutex);
695
696 qDebug() <<"===";
697 Q_FOREACH (KisStrokeSP stroke, m_d->strokesQueue) {
698 qDebug() << ppVar(stroke->name()) << ppVar(stroke->type()) << ppVar(stroke->numJobs()) << ppVar(stroke->isInitialized()) << ppVar(stroke->isCancelled());
699 }
700 qDebug() <<"===";
701}
702
704{
705 m_d->lod0ToNStrokeStrategyFactory = factory;
706}
707
709{
710 m_d->suspendResumeUpdatesStrokeStrategyFactory = factory;
711}
712
713void KisStrokesQueue::setPurgeRedoStateCallback(const std::function<void ()> &callback)
714{
715 m_d->purgeRedoStateCallback = callback;
716}
717
719{
720 m_d->postSyncLod0GUIPlaneRequestForResume = callback;
721}
722
724{
725 return &m_d->lodNPostExecutionUndoAdapter;
726}
727
729{
730 QMutexLocker locker(&m_d->mutex);
731 if(m_d->strokesQueue.isEmpty()) return KUndo2MagicString();
732
733 return m_d->strokesQueue.head()->name();
734}
735
737{
738 QMutexLocker locker(&m_d->mutex);
739 return m_d->openedStrokesCounter;
740}
741
743 bool externalJobsPending)
744{
745 if(m_d->strokesQueue.isEmpty()) return false;
746 bool result = false;
747
748 const int levelOfDetail = updaterContext.currentLevelOfDetail();
749
750 const KisUpdaterContextSnapshotEx snapshot = updaterContext.getContextSnapshotEx();
751
752 const bool hasStrokeJobs = !(snapshot == ContextEmpty ||
753 snapshot == HasMergeJob);
754 const bool hasMergeJobs = snapshot & HasMergeJob;
755
756 if(checkStrokeState(hasStrokeJobs, levelOfDetail) &&
757 checkExclusiveProperty(hasMergeJobs, hasStrokeJobs) &&
758 checkSequentialProperty(snapshot, externalJobsPending)) {
759
760 KisStrokeSP stroke = m_d->strokesQueue.head();
761 updaterContext.addStrokeJob(stroke->popOneJob());
762 result = true;
763 }
764
765 return result;
766}
767
768void KisStrokesQueue::Private::loadStroke(KisStrokeSP stroke)
769{
770 needsExclusiveAccess = stroke->isExclusive();
771 wrapAroundModeSupported = stroke->supportsWrapAroundMode();
772 balancingRatioOverride = stroke->balancingRatioOverride();
773 currentStrokeLoaded = true;
774
781 if (purgeRedoStateCallback &&
782 stroke->clearsRedoOnStart()) {
783
784 purgeRedoStateCallback();
785 }
786}
787
788bool KisStrokesQueue::checkStrokeState(bool hasStrokeJobsRunning,
789 int runningLevelOfDetail)
790{
791 KisStrokeSP stroke = m_d->strokesQueue.head();
792 bool result = false;
793
798 bool hasLodCompatibility = checkLevelOfDetailProperty(runningLevelOfDetail);
799 bool hasJobs = stroke->hasJobs();
800
810 if(!stroke->isInitialized() && hasJobs && hasLodCompatibility) {
816 if (!m_d->currentStrokeLoaded) {
817 m_d->loadStroke(stroke);
818 }
819
820 result = true;
821 }
822 else if(hasJobs && hasLodCompatibility) {
827 if (!m_d->currentStrokeLoaded) {
828 m_d->loadStroke(stroke);
829 }
830
831 result = true;
832 }
833 else if(stroke->isEnded() && !hasJobs && !hasStrokeJobsRunning) {
834 m_d->tryClearUndoOnStrokeCompletion(stroke);
835
836 const bool needsSyncLod0PlaneToGUI =
837 stroke->type() == KisStroke::LOD0 &&
838 stroke->isCancelled();
839
846 if (needsSyncLod0PlaneToGUI &&
847 m_d->postSyncLod0GUIPlaneRequestForResume) {
848 m_d->postSyncLod0GUIPlaneRequestForResume();
849 }
850
851 m_d->strokesQueue.dequeue(); // deleted by shared pointer
852 m_d->needsExclusiveAccess = false;
853 m_d->wrapAroundModeSupported = false;
854 m_d->balancingRatioOverride = -1.0;
855 m_d->currentStrokeLoaded = false;
856
857 m_d->switchDesiredLevelOfDetail(false);
858
859 if(!m_d->strokesQueue.isEmpty()) {
860 result = checkStrokeState(false, runningLevelOfDetail);
861 }
862 }
863
864 return result;
865}
866
868 bool hasStrokeJobs)
869{
870 Q_UNUSED(hasStrokeJobs);
871
872 if(!m_d->strokesQueue.head()->isExclusive()) return true;
873 return hasMergeJobs == 0;
874}
875
876bool KisStrokesQueue::checkSequentialProperty(KisUpdaterContextSnapshotEx snapshot,
877 bool externalJobsPending)
878{
879 KisStrokeSP stroke = m_d->strokesQueue.head();
880
881 if (snapshot & HasSequentialJob ||
882 snapshot & HasBarrierJob) {
883 return false;
884 }
885
886 KisStrokeJobData::Sequentiality nextSequentiality =
887 stroke->nextJobSequentiality();
888
889 if (nextSequentiality == KisStrokeJobData::UNIQUELY_CONCURRENT &&
890 snapshot & HasUniquelyConcurrentJob) {
891
892 return false;
893 }
894
895 if (nextSequentiality == KisStrokeJobData::SEQUENTIAL &&
896 (snapshot & HasUniquelyConcurrentJob ||
897 snapshot & HasConcurrentJob)) {
898
899 return false;
900 }
901
902 if (nextSequentiality == KisStrokeJobData::BARRIER &&
903 (snapshot & HasUniquelyConcurrentJob ||
904 snapshot & HasConcurrentJob ||
905 snapshot & HasMergeJob ||
906 externalJobsPending)) {
907
908 return false;
909 }
910
911 return true;
912}
913
915{
916 KisStrokeSP stroke = m_d->strokesQueue.head();
917
918 return runningLevelOfDetail < 0 ||
919 stroke->nextJobLevelOfDetail() == runningLevelOfDetail;
920}
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)