File indexing completed on 2024-06-09 04:22:18

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_update_scheduler_test.h"
0008 #include <simpletest.h>
0009 
0010 #include <KoColorSpace.h>
0011 #include <KoColorSpaceRegistry.h>
0012 
0013 #include "kis_group_layer.h"
0014 #include "kis_paint_layer.h"
0015 #include "kis_adjustment_layer.h"
0016 #include "filter/kis_filter.h"
0017 #include "filter/kis_filter_configuration.h"
0018 #include "filter/kis_filter_registry.h"
0019 #include "kis_selection.h"
0020 
0021 #include "scheduler_utils.h"
0022 #include "kis_update_scheduler.h"
0023 #include "kis_updater_context.h"
0024 #include "kis_update_job_item.h"
0025 #include "kis_simple_update_queue.h"
0026 #include <KisGlobalResourcesInterface.h>
0027 
0028 #include "../../sdk/tests/testutil.h"
0029 #include "kistest.h"
0030 
0031 
0032 KisImageSP KisUpdateSchedulerTest::buildTestingImage()
0033 {
0034     QImage sourceImage1(QString(FILES_DATA_DIR) + '/' + "hakonepa.png");
0035     QImage sourceImage2(QString(FILES_DATA_DIR) + '/' + "inverted_hakonepa.png");
0036 
0037     QRect imageRect = QRect(QPoint(0,0), sourceImage1.size());
0038 
0039     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
0040     KisImageSP image = new KisImage(0, imageRect.width(), imageRect.height(), cs, "merge test");
0041 
0042     KisFilterSP filter = KisFilterRegistry::instance()->value("blur");
0043     Q_ASSERT(filter);
0044     KisFilterConfigurationSP configuration = filter->defaultConfiguration(KisGlobalResourcesInterface::instance());
0045     Q_ASSERT(configuration);
0046 
0047     KisPaintLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
0048     KisPaintLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8 / 3);
0049     KisLayerSP blur1 = new KisAdjustmentLayer(image, "blur1", configuration->cloneWithResourcesSnapshot(), 0);
0050 
0051     paintLayer1->paintDevice()->convertFromQImage(sourceImage1, 0, 0, 0);
0052     paintLayer2->paintDevice()->convertFromQImage(sourceImage2, 0, 0, 0);
0053 
0054     image->barrierLock();
0055     image->addNode(paintLayer1);
0056     image->addNode(paintLayer2);
0057     image->addNode(blur1);
0058     image->unlock();
0059 
0060     return image;
0061 }
0062 
0063 void KisUpdateSchedulerTest::testMerge()
0064 {
0065     KisImageSP image = buildTestingImage();
0066     QRect imageRect = image->bounds();
0067     KisNodeSP rootLayer = image->rootLayer();
0068     KisNodeSP paintLayer1 = rootLayer->firstChild();
0069 
0070     QCOMPARE(paintLayer1->name(), QString("paint1"));
0071 
0072 
0073     KisUpdateScheduler scheduler(image.data());
0074 
0075     /**
0076      * Test synchronous Full Refresh
0077      */
0078 
0079     scheduler.fullRefresh(rootLayer, image->bounds(), image->bounds());
0080     QCOMPARE(rootLayer->exactBounds(), image->bounds());
0081 
0082     QImage resultFRProjection = rootLayer->projection()->convertToQImage(0);
0083     resultFRProjection.save(QString(FILES_OUTPUT_DIR) + '/' + "scheduler_fr_merge_result.png");
0084 
0085     /**
0086      * Test incremental updates
0087      */
0088 
0089     rootLayer->projection()->clear();
0090 
0091     const qint32 num = 4;
0092     qint32 width = imageRect.width() / num;
0093     qint32 lastWidth = imageRect.width() - width;
0094 
0095     QVector<QRect> dirtyRects(num);
0096 
0097     for(qint32 i = 0; i < num-1; i++) {
0098         dirtyRects[i] = QRect(width*i, 0, width, imageRect.height());
0099     }
0100     dirtyRects[num-1] = QRect(width*(num-1), 0, lastWidth, imageRect.height());
0101 
0102     for(qint32 i = 0; i < num; i+=2) {
0103         scheduler.updateProjection(paintLayer1, dirtyRects[i], image->bounds());
0104     }
0105 
0106     for(qint32 i = 1; i < num; i+=2) {
0107         scheduler.updateProjection(paintLayer1, dirtyRects[i], image->bounds());
0108     }
0109 
0110     scheduler.waitForDone();
0111 
0112     QCOMPARE(rootLayer->exactBounds(), image->bounds());
0113 
0114     QImage resultDirtyProjection = rootLayer->projection()->convertToQImage(0);
0115     resultDirtyProjection.save(QString(FILES_OUTPUT_DIR) + '/' + "scheduler_dp_merge_result.png");
0116 
0117     QPoint pt;
0118     QVERIFY(TestUtil::compareQImages(pt, resultFRProjection, resultDirtyProjection));
0119 }
0120 
0121 void KisUpdateSchedulerTest::benchmarkOverlappedMerge()
0122 {
0123     KisImageSP image = buildTestingImage();
0124     KisNodeSP rootLayer = image->rootLayer();
0125     KisNodeSP paintLayer1 = rootLayer->firstChild();
0126     QRect imageRect = image->bounds();
0127 
0128     QCOMPARE(paintLayer1->name(), QString("paint1"));
0129     QCOMPARE(imageRect, QRect(0,0,640,441));
0130 
0131     KisUpdateScheduler scheduler(image.data());
0132 
0133     const qint32 xShift = 10;
0134     const qint32 yShift = 0;
0135     const qint32 numShifts = 64;
0136 
0137     QBENCHMARK{
0138         QRect dirtyRect(0, 0, 200, imageRect.height());
0139 
0140         for(int i = 0; i < numShifts; i++) {
0141             // dbgKrita << dirtyRect;
0142             scheduler.updateProjection(paintLayer1, dirtyRect, image->bounds());
0143             dirtyRect.translate(xShift, yShift);
0144         }
0145 
0146         scheduler.waitForDone();
0147     }
0148 }
0149 
0150 void KisUpdateSchedulerTest::testLocking()
0151 {
0152     KisImageSP image = buildTestingImage();
0153     KisNodeSP rootLayer = image->rootLayer();
0154     KisNodeSP paintLayer1 = rootLayer->firstChild();
0155     QRect imageRect = image->bounds();
0156 
0157     QCOMPARE(paintLayer1->name(), QString("paint1"));
0158     QCOMPARE(imageRect, QRect(0,0,640,441));
0159 
0160     KisTestableUpdateScheduler scheduler(image.data(), 2);
0161     KisUpdaterContext *context = scheduler.updaterContext();
0162 
0163     QVector<KisUpdateJobItem*> jobs;
0164 
0165     QRect dirtyRect1(0,0,50,100);
0166     QRect dirtyRect2(0,0,100,100);
0167     QRect dirtyRect3(50,0,50,100);
0168     QRect dirtyRect4(150,150,50,50);
0169 
0170     scheduler.updateProjection(paintLayer1, imageRect, imageRect);
0171 
0172     jobs = context->getJobs();
0173     QCOMPARE(jobs[0]->isRunning(), true);
0174     QCOMPARE(jobs[1]->isRunning(), false);
0175     QVERIFY(checkWalker(jobs[0]->walker(), imageRect));
0176 
0177     context->clear();
0178 
0179     scheduler.immediateLockForReadOnly();
0180 
0181     scheduler.updateProjection(paintLayer1, dirtyRect1, imageRect);
0182     scheduler.updateProjection(paintLayer1, dirtyRect2, imageRect);
0183     scheduler.updateProjection(paintLayer1, dirtyRect3, imageRect);
0184     scheduler.updateProjection(paintLayer1, dirtyRect4, imageRect);
0185 
0186     jobs = context->getJobs();
0187     QCOMPARE(jobs[0]->isRunning(), false);
0188     QCOMPARE(jobs[1]->isRunning(), false);
0189 
0190     scheduler.unlock();
0191 
0192     jobs = context->getJobs();
0193     QCOMPARE(jobs[0]->isRunning(), true);
0194     QCOMPARE(jobs[1]->isRunning(), true);
0195     QVERIFY(checkWalker(jobs[0]->walker(), dirtyRect2));
0196     QVERIFY(checkWalker(jobs[1]->walker(), dirtyRect4));
0197 }
0198 
0199 void KisUpdateSchedulerTest::testExclusiveStrokes()
0200 {
0201     KisImageSP image = buildTestingImage();
0202     KisNodeSP rootLayer = image->rootLayer();
0203     KisNodeSP paintLayer1 = rootLayer->firstChild();
0204     QRect imageRect = image->bounds();
0205 
0206     QCOMPARE(paintLayer1->name(), QString("paint1"));
0207     QCOMPARE(imageRect, QRect(0,0,640,441));
0208 
0209     QRect dirtyRect1(0,0,50,100);
0210 
0211     KisTestableUpdateScheduler scheduler(image.data(), 2);
0212     KisUpdaterContext *context = scheduler.updaterContext();
0213     QVector<KisUpdateJobItem*> jobs;
0214 
0215     scheduler.updateProjection(paintLayer1, dirtyRect1, imageRect);
0216 
0217     jobs = context->getJobs();
0218     QCOMPARE(jobs[0]->isRunning(), true);
0219     QCOMPARE(jobs[1]->isRunning(), false);
0220     QVERIFY(checkWalker(jobs[0]->walker(), dirtyRect1));
0221 
0222     KisStrokeId id = scheduler.startStroke(new KisTestingStrokeStrategy(QLatin1String("excl_"), true, false));
0223 
0224     jobs = context->getJobs();
0225     QCOMPARE(jobs[0]->isRunning(), true);
0226     QCOMPARE(jobs[1]->isRunning(), false);
0227     QVERIFY(checkWalker(jobs[0]->walker(), dirtyRect1));
0228 
0229     context->clear();
0230     scheduler.endStroke(id);
0231 
0232     jobs = context->getJobs();
0233     QCOMPARE(jobs[0]->isRunning(), true);
0234     QCOMPARE(jobs[1]->isRunning(), false);
0235     COMPARE_NAME(jobs[0], "excl_init");
0236 
0237     scheduler.updateProjection(paintLayer1, dirtyRect1, imageRect);
0238 
0239     jobs = context->getJobs();
0240     QCOMPARE(jobs[0]->isRunning(), true);
0241     QCOMPARE(jobs[1]->isRunning(), false);
0242     COMPARE_NAME(jobs[0], "excl_init");
0243 
0244     context->clear();
0245     scheduler.processQueues();
0246 
0247     jobs = context->getJobs();
0248     QCOMPARE(jobs[0]->isRunning(), true);
0249     QCOMPARE(jobs[1]->isRunning(), false);
0250     COMPARE_NAME(jobs[0], "excl_finish");
0251 
0252     context->clear();
0253     scheduler.processQueues();
0254 
0255     jobs = context->getJobs();
0256     QCOMPARE(jobs[0]->isRunning(), true);
0257     QCOMPARE(jobs[1]->isRunning(), false);
0258     QVERIFY(checkWalker(jobs[0]->walker(), dirtyRect1));
0259 }
0260 
0261 void KisUpdateSchedulerTest::testEmptyStroke()
0262 {
0263     KisImageSP image = buildTestingImage();
0264 
0265     KisStrokeId id = image->startStroke(new KisStrokeStrategy(QLatin1String()));
0266     image->addJob(id, 0);
0267     image->endStroke(id);
0268     image->waitForDone();
0269 }
0270 
0271 #include "kis_lazy_wait_condition.h"
0272 
0273 void KisUpdateSchedulerTest::testLazyWaitCondition()
0274 {
0275     {
0276         dbgKrita << "Not initialized";
0277         KisLazyWaitCondition condition;
0278         QVERIFY(!condition.wait(50));
0279     }
0280 
0281     {
0282         dbgKrita << "Initialized, not awake";
0283         KisLazyWaitCondition condition;
0284         condition.initWaiting();
0285         QVERIFY(!condition.wait(50));
0286         condition.endWaiting();
0287     }
0288 
0289     {
0290         dbgKrita << "Initialized, awake";
0291         KisLazyWaitCondition condition;
0292         condition.initWaiting();
0293         condition.wakeAll();
0294         QVERIFY(condition.wait(50));
0295         condition.endWaiting();
0296     }
0297 
0298     {
0299         dbgKrita << "Initialized, not awake, then awake";
0300         KisLazyWaitCondition condition;
0301         condition.initWaiting();
0302         QVERIFY(!condition.wait(50));
0303         condition.wakeAll();
0304         QVERIFY(condition.wait(50));
0305         condition.endWaiting();
0306     }
0307 
0308     {
0309         dbgKrita << "Doublewait";
0310         KisLazyWaitCondition condition;
0311         condition.initWaiting();
0312         condition.initWaiting();
0313         QVERIFY(!condition.wait(50));
0314         condition.wakeAll();
0315         QVERIFY(condition.wait(50));
0316         QVERIFY(condition.wait(50));
0317         condition.endWaiting();
0318     }
0319 }
0320 
0321 #define NUM_THREADS 10
0322 #define NUM_CYCLES 500
0323 #define NTH_CHECK 3
0324 
0325 class UpdatesBlockTester : public QRunnable
0326 {
0327 public:
0328     UpdatesBlockTester(KisUpdateScheduler *scheduler, KisNodeSP node)
0329         : m_scheduler(scheduler), m_node(node)
0330     {
0331     }
0332 
0333     void run() override {
0334         for (int i = 0; i < NUM_CYCLES; i++) {
0335             if(i % NTH_CHECK == 0) {
0336                 m_scheduler->blockUpdates();
0337                 QTest::qSleep(1); // a bit of salt for crashiness ;)
0338                 Q_ASSERT(!m_scheduler->haveUpdatesRunning());
0339                 m_scheduler->unblockUpdates();
0340             }
0341             else {
0342                 QRect updateRect(0,0,100,100);
0343                 updateRect.moveTopLeft(QPoint((i%10)*100, (i%10)*100));
0344                 m_scheduler->updateProjection(m_node, updateRect, QRect(0,0,1100,1100));
0345             }
0346         }
0347     }
0348 private:
0349     KisUpdateScheduler *m_scheduler;
0350     KisNodeSP m_node;
0351 };
0352 
0353 void KisUpdateSchedulerTest::testBlockUpdates()
0354 {
0355     KisImageSP image = buildTestingImage();
0356     KisNodeSP rootLayer = image->rootLayer();
0357     KisNodeSP paintLayer1 = rootLayer->firstChild();
0358 
0359     KisUpdateScheduler scheduler(image.data());
0360 
0361     QThreadPool threadPool;
0362     threadPool.setMaxThreadCount(NUM_THREADS);
0363 
0364     for(int i = 0; i< NUM_THREADS; i++) {
0365         UpdatesBlockTester *tester =
0366             new UpdatesBlockTester(&scheduler, paintLayer1);
0367 
0368         threadPool.start(tester);
0369     }
0370 
0371     threadPool.waitForDone();
0372 }
0373 
0374 #include "kis_update_time_monitor.h"
0375 
0376 void KisUpdateSchedulerTest::testTimeMonitor()
0377 {
0378     QVector<QRect> dirtyRects;
0379 
0380     KisUpdateTimeMonitor::instance()->startStrokeMeasure();
0381     KisUpdateTimeMonitor::instance()->reportMouseMove(QPointF(100, 0));
0382 
0383     KisUpdateTimeMonitor::instance()->reportJobStarted((void*) 10);
0384     QTest::qSleep(300);
0385     KisUpdateTimeMonitor::instance()->reportJobStarted((void*) 20);
0386     QTest::qSleep(100);
0387     dirtyRects << QRect(10,10,10,10);
0388     KisUpdateTimeMonitor::instance()->reportJobFinished((void*) 10, dirtyRects);
0389     QTest::qSleep(100);
0390     dirtyRects.clear();
0391     dirtyRects << QRect(30,30,10,10);
0392     KisUpdateTimeMonitor::instance()->reportJobFinished((void*) 20, dirtyRects);
0393     QTest::qSleep(500);
0394     KisUpdateTimeMonitor::instance()->reportUpdateFinished(QRect(10,10,10,10));
0395     QTest::qSleep(300);
0396     KisUpdateTimeMonitor::instance()->reportUpdateFinished(QRect(30,30,10,10));
0397 
0398     KisUpdateTimeMonitor::instance()->reportMouseMove(QPointF(130, 0));
0399 
0400     KisUpdateTimeMonitor::instance()->endStrokeMeasure();
0401 }
0402 
0403 void KisUpdateSchedulerTest::testLodSync()
0404 {
0405     KisImageSP image = buildTestingImage();
0406     KisNodeSP rootLayer = image->root();
0407     KisNodeSP paintLayer1 = rootLayer->firstChild();
0408 
0409     QCOMPARE(paintLayer1->name(), QString("paint1"));
0410 
0411     image->setLodPreferences(KisLodPreferences(2));
0412 
0413     image->explicitRegenerateLevelOfDetail();
0414 
0415     image->waitForDone();
0416 }
0417 
0418 KISTEST_MAIN(KisUpdateSchedulerTest)
0419