File indexing completed on 2024-12-22 04:10:27

0001 /*
0002  *  SPDX-FileCopyrightText: 2010 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_updater_context_test.h"
0008 #include <simpletest.h>
0009 
0010 #include "kistest.h"
0011 
0012 #include <QAtomicInt>
0013 #include <KoColorSpace.h>
0014 #include <KoColorSpaceRegistry.h>
0015 
0016 #include "kis_paint_layer.h"
0017 
0018 #include "kis_merge_walker.h"
0019 #include "kis_updater_context.h"
0020 #include "kis_image.h"
0021 
0022 #include "scheduler_utils.h"
0023 
0024 #include "lod_override.h"
0025 #include "config-limit-long-tests.h"
0026 
0027 void KisUpdaterContextTest::testJobInterference()
0028 {
0029     KisTestableUpdaterContext context(3);
0030 
0031     QRect imageRect(0,0,100,100);
0032 
0033     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
0034     KisImageSP image = new KisImage(0, imageRect.width(), imageRect.height(), cs, "merge test");
0035 
0036     KisPaintLayerSP paintLayer = new KisPaintLayer(image, "test", OPACITY_OPAQUE_U8);
0037 
0038     image->barrierLock();
0039     image->addNode(paintLayer);
0040     image->unlock();
0041 
0042     QRect dirtyRect1(0,0,50,100);
0043     KisBaseRectsWalkerSP walker1 = new KisMergeWalker(imageRect);
0044     walker1->collectRects(paintLayer, dirtyRect1);
0045 
0046     context.lock();
0047     context.addMergeJob(walker1);
0048     context.unlock();
0049 
0050     // overlapping job --- forbidden
0051     {
0052         QRect dirtyRect(30,0,100,100);
0053         KisBaseRectsWalkerSP walker = new KisMergeWalker(imageRect);
0054         walker->collectRects(paintLayer, dirtyRect);
0055 
0056         context.lock();
0057         QVERIFY(!context.isJobAllowed(walker));
0058         context.unlock();
0059     }
0060 
0061     // not overlapping job --- allowed
0062     {
0063         QRect dirtyRect(60,0,100,100);
0064         KisBaseRectsWalkerSP walker = new KisMergeWalker(imageRect);
0065         walker->collectRects(paintLayer, dirtyRect);
0066 
0067         context.lock();
0068         QVERIFY(context.isJobAllowed(walker));
0069         context.unlock();
0070     }
0071 
0072     // not overlapping job, conflicting LOD --- forbidden
0073     {
0074         TestUtil::LodOverride l(1, image);
0075 
0076         QCOMPARE(paintLayer->paintDevice()->defaultBounds()->currentLevelOfDetail(), 1);
0077 
0078         QRect dirtyRect(60,0,100,100);
0079         KisBaseRectsWalkerSP walker = new KisMergeWalker(imageRect);
0080         walker->collectRects(paintLayer, dirtyRect);
0081 
0082         context.lock();
0083         QVERIFY(!context.isJobAllowed(walker));
0084         context.unlock();
0085     }
0086 }
0087 
0088 void KisUpdaterContextTest::testSnapshot()
0089 {
0090     KisTestableUpdaterContext context(3);
0091 
0092     QRect imageRect(0,0,100,100);
0093 
0094     KisBaseRectsWalkerSP walker1 = new KisMergeWalker(imageRect);
0095 
0096     qint32 numMergeJobs = -777;
0097     qint32 numStrokeJobs = -777;
0098 
0099     context.lock();
0100 
0101     context.getJobsSnapshot(numMergeJobs, numStrokeJobs);
0102     QCOMPARE(numMergeJobs, 0);
0103     QCOMPARE(numStrokeJobs, 0);
0104     QCOMPARE(context.currentLevelOfDetail(), -1);
0105 
0106     context.addMergeJob(walker1);
0107     context.getJobsSnapshot(numMergeJobs, numStrokeJobs);
0108     QCOMPARE(numMergeJobs, 1);
0109     QCOMPARE(numStrokeJobs, 0);
0110     QCOMPARE(context.currentLevelOfDetail(), 0);
0111 
0112 
0113     KisStrokeJobData *data =
0114         new KisStrokeJobData(KisStrokeJobData::SEQUENTIAL,
0115                              KisStrokeJobData::NORMAL);
0116 
0117     QScopedPointer<KisStrokeJobStrategy> strategy(
0118         new KisNoopDabStrategy("test"));
0119 
0120     context.addStrokeJob(new KisStrokeJob(strategy.data(), data, 0, true));
0121     context.getJobsSnapshot(numMergeJobs, numStrokeJobs);
0122     QCOMPARE(numMergeJobs, 1);
0123     QCOMPARE(numStrokeJobs, 1);
0124     QCOMPARE(context.currentLevelOfDetail(), 0);
0125 
0126 
0127     context.addSpontaneousJob(new KisNoopSpontaneousJob());
0128     context.getJobsSnapshot(numMergeJobs, numStrokeJobs);
0129     QCOMPARE(numMergeJobs, 2);
0130     QCOMPARE(numStrokeJobs, 1);
0131     QCOMPARE(context.currentLevelOfDetail(), 0);
0132 
0133     context.unlock();
0134 
0135     {
0136         context.lock();
0137         context.clear();
0138 
0139         context.getJobsSnapshot(numMergeJobs, numStrokeJobs);
0140         QCOMPARE(numMergeJobs, 0);
0141         QCOMPARE(numStrokeJobs, 0);
0142         QCOMPARE(context.currentLevelOfDetail(), -1);
0143 
0144         data =
0145             new KisStrokeJobData(KisStrokeJobData::SEQUENTIAL,
0146                                  KisStrokeJobData::NORMAL);
0147 
0148         context.addStrokeJob(new KisStrokeJob(strategy.data(), data, 2, true));
0149         context.getJobsSnapshot(numMergeJobs, numStrokeJobs);
0150         QCOMPARE(numMergeJobs, 0);
0151         QCOMPARE(numStrokeJobs, 1);
0152         QCOMPARE(context.currentLevelOfDetail(), 2);
0153 
0154         context.unlock();
0155     }
0156 }
0157 
0158 #define NUM_THREADS 10
0159 #ifdef LIMIT_LONG_TESTS
0160 #   define NUM_JOBS 60
0161 #else
0162 #   define NUM_JOBS 6000
0163 #endif
0164 #define EXCLUSIVE_NTH 3
0165 #define NUM_CHECKS 10
0166 #define CHECK_DELAY 3 // ms
0167 
0168 class ExclusivenessCheckerStrategy : public KisStrokeJobStrategy
0169 {
0170 public:
0171     ExclusivenessCheckerStrategy(QAtomicInt &counter,
0172                                  QAtomicInt &hadConcurrency)
0173         : m_counter(counter),
0174           m_hadConcurrency(hadConcurrency)
0175     {
0176     }
0177 
0178     void run(KisStrokeJobData *data) override {
0179         Q_UNUSED(data);
0180 
0181         m_counter.ref();
0182 
0183         for(int i = 0; i < NUM_CHECKS; i++) {
0184             if(data->isExclusive()) {
0185                 Q_ASSERT(m_counter == 1);
0186             }
0187             else if (m_counter > 1) {
0188                 m_hadConcurrency.ref();
0189             }
0190             QTest::qSleep(CHECK_DELAY);
0191         }
0192 
0193         m_counter.deref();
0194     }
0195 
0196     QString debugId() const override {
0197         return "ExclusivenessCheckerStrategy";
0198     }
0199 
0200 private:
0201     QAtomicInt &m_counter;
0202     QAtomicInt &m_hadConcurrency;
0203 };
0204 
0205 void KisUpdaterContextTest::stressTestExclusiveJobs()
0206 {
0207     KisUpdaterContext context(NUM_THREADS);
0208     QAtomicInt counter;
0209     QAtomicInt hadConcurrency;
0210 
0211     for(int i = 0; i < NUM_JOBS; i++) {
0212         if(context.hasSpareThread()) {
0213             bool isExclusive = i % EXCLUSIVE_NTH == 0;
0214 
0215             KisStrokeJobData *data =
0216                 new KisStrokeJobData(KisStrokeJobData::SEQUENTIAL,
0217                                      isExclusive ?
0218                                      KisStrokeJobData::EXCLUSIVE :
0219                                      KisStrokeJobData::NORMAL);
0220 
0221             KisStrokeJobStrategy *strategy =
0222                 new ExclusivenessCheckerStrategy(counter, hadConcurrency);
0223 
0224             context.addStrokeJob(new KisStrokeJob(strategy, data, 0, true));
0225         }
0226         else {
0227             QTest::qSleep(CHECK_DELAY);
0228         }
0229     }
0230 
0231     context.waitForDone();
0232 
0233     QVERIFY(!counter);
0234     dbgKrita << "Concurrency observed:" << hadConcurrency
0235              << "/" << NUM_CHECKS * NUM_JOBS;
0236 }
0237 
0238 KISTEST_MAIN(KisUpdaterContextTest)
0239