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