File indexing completed on 2024-11-10 04:00:28
0001 /* 0002 * SPDX-FileCopyrightText: 2012 Dmitry Kazakov <dimula73@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "kis_low_memory_benchmark.h" 0008 0009 #include <simpletest.h> 0010 0011 #include "kis_benchmark_values.h" 0012 0013 #include <KoColor.h> 0014 #include <KoColorSpace.h> 0015 #include <KoColorSpaceRegistry.h> 0016 0017 #include <kis_image.h> 0018 #include <kis_layer.h> 0019 #include <kis_paint_layer.h> 0020 #include "kis_paint_device.h" 0021 #include "kis_painter.h" 0022 0023 #include <brushengine/kis_paint_information.h> 0024 #include <brushengine/kis_paintop_registry.h> 0025 #include <brushengine/kis_paintop_preset.h> 0026 0027 #include "KisGlobalResourcesInterface.h" 0028 0029 #include "tiles3/kis_tile_data_store.h" 0030 #include "kis_surrogate_undo_adapter.h" 0031 #include "kis_image_config.h" 0032 #define LOAD_PRESET_OR_RETURN(preset, fileName) \ 0033 if(!preset->load(KisGlobalResourcesInterface::instance())) { dbgKrita << "Preset" << fileName << "was NOT loaded properly. Done."; return; } \ 0034 else dbgKrita << "Loaded preset:" << fileName 0035 0036 #define HUGE_IMAGE_SIZE 8000 0037 0038 /** 0039 * This benchmark runs a series of huge strokes on a canvas with a 0040 * particular configuration of the swapper/pooler and history 0041 * management. After the test is done you can visualize the results 0042 * with the GNU Octave. Please use kis_low_memory_show_report.m file 0043 * for that. 0044 */ 0045 void KisLowMemoryBenchmark::benchmarkWideArea(const QString presetFileName, 0046 const QRectF &rect, qreal vstep, 0047 int numCycles, 0048 bool createTransaction, 0049 int hardLimitMiB, 0050 int softLimitMiB, 0051 int poolLimitMiB, 0052 int index) 0053 { 0054 KisPaintOpPresetSP preset(new KisPaintOpPreset(QString(FILES_DATA_DIR) + '/' + presetFileName)); 0055 LOAD_PRESET_OR_RETURN(preset, presetFileName); 0056 0057 0058 /** 0059 * Initialize image and painter 0060 */ 0061 const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->rgb8(); 0062 KisImageSP image = new KisImage(0, HUGE_IMAGE_SIZE, HUGE_IMAGE_SIZE, colorSpace, "stroke sample image"); 0063 KisLayerSP layer = new KisPaintLayer(image, "temporary for stroke sample", OPACITY_OPAQUE_U8, colorSpace); 0064 KisLayerSP layerExtra = new KisPaintLayer(image, "temporary for threading", OPACITY_OPAQUE_U8, colorSpace); 0065 0066 image->addNode(layer, image->root()); 0067 image->addNode(layerExtra, image->root()); 0068 0069 KisPainter *painter = new KisPainter(layer->paintDevice()); 0070 0071 painter->setPaintColor(KoColor(Qt::black, colorSpace)); 0072 painter->setPaintOpPreset(preset, layer, image); 0073 0074 /** 0075 * A simple adapter that will store all the transactions for us 0076 */ 0077 KisSurrogateUndoAdapter undoAdapter; 0078 0079 /** 0080 * Reset configuration to the desired settings 0081 */ 0082 KisImageConfig config(false); 0083 qreal oldHardLimit = config.memoryHardLimitPercent(); 0084 qreal oldSoftLimit = config.memorySoftLimitPercent(); 0085 qreal oldPoolLimit = config.memoryPoolLimitPercent(); 0086 const qreal _MiB = 100.0 / KisImageConfig::totalRAM(); 0087 0088 config.setMemoryHardLimitPercent(hardLimitMiB * _MiB); 0089 config.setMemorySoftLimitPercent(softLimitMiB * _MiB); 0090 config.setMemoryPoolLimitPercent(poolLimitMiB * _MiB); 0091 0092 KisTileDataStore::instance()->testingRereadConfig(); 0093 0094 /** 0095 * Create an empty the log file 0096 */ 0097 QString fileName; 0098 fileName = QString("log_%1_%2_%3_%4_%5.txt") 0099 .arg(createTransaction) 0100 .arg(hardLimitMiB) 0101 .arg(softLimitMiB) 0102 .arg(poolLimitMiB) 0103 .arg(index); 0104 0105 QFile logFile(fileName); 0106 logFile.open(QFile::WriteOnly | QFile::Truncate); 0107 QTextStream logStream(&logFile); 0108 logStream.setCodec("UTF-8"); 0109 logStream.setFieldWidth(10); 0110 logStream.setFieldAlignment(QTextStream::AlignRight); 0111 0112 /** 0113 * Start painting on the image 0114 */ 0115 0116 QElapsedTimer cycleTime; 0117 QElapsedTimer lineTime; 0118 cycleTime.start(); 0119 lineTime.start(); 0120 0121 qreal rectBottom = rect.y() + rect.height(); 0122 0123 for (int i = 0; i < numCycles; i++) { 0124 cycleTime.restart(); 0125 0126 QLineF line(rect.topLeft(), rect.topLeft() + QPointF(rect.width(), 0)); 0127 if (createTransaction) { 0128 painter->beginTransaction(); 0129 } 0130 0131 KisDistanceInformation currentDistance; 0132 0133 while(line.y1() < rectBottom) { 0134 lineTime.restart(); 0135 0136 KisPaintInformation pi1(line.p1(), 0.0); 0137 KisPaintInformation pi2(line.p2(), 1.0); 0138 painter->paintLine(pi1, pi2, ¤tDistance); 0139 painter->device()->setDirty(painter->takeDirtyRegion()); 0140 0141 logStream << "L 1" << i << lineTime.elapsed() 0142 << KisTileDataStore::instance()->numTilesInMemory() * 16 0143 << KisTileDataStore::instance()->numTiles() * 16 0144 << createTransaction << endl; 0145 0146 line.translate(0, vstep); 0147 } 0148 0149 painter->device()->setDirty(painter->takeDirtyRegion()); 0150 0151 if (createTransaction) { 0152 painter->endTransaction(&undoAdapter); 0153 } 0154 0155 // comment/uncomment to emulate user waiting after the stroke 0156 QTest::qSleep(1000); 0157 0158 logStream << "C 2" << i << cycleTime.elapsed() 0159 << KisTileDataStore::instance()->numTilesInMemory() * 16 0160 << KisTileDataStore::instance()->numTiles() * 16 0161 << createTransaction 0162 << config.memoryHardLimitPercent() / _MiB 0163 << config.memorySoftLimitPercent() / _MiB 0164 << config.memoryPoolLimitPercent() / _MiB << endl; 0165 } 0166 0167 config.setMemoryHardLimitPercent(oldHardLimit * _MiB); 0168 config.setMemorySoftLimitPercent(oldSoftLimit * _MiB); 0169 config.setMemoryPoolLimitPercent(oldPoolLimit * _MiB); 0170 0171 delete painter; 0172 } 0173 0174 void KisLowMemoryBenchmark::unlimitedMemoryNoHistoryNoPool() 0175 { 0176 QString presetFileName = "autobrush_300px.kpp"; 0177 // one cycle takes about 48 MiB of memory (total 960 MiB) 0178 QRectF rect(150,150,4000,4000); 0179 qreal step = 250; 0180 int numCycles = 20; 0181 0182 benchmarkWideArea(presetFileName, rect, step, numCycles, false, 0183 3000, 3000, 0, 0); 0184 } 0185 0186 void KisLowMemoryBenchmark::unlimitedMemoryHistoryNoPool() 0187 { 0188 QString presetFileName = "autobrush_300px.kpp"; 0189 // one cycle takes about 48 MiB of memory (total 960 MiB) 0190 QRectF rect(150,150,4000,4000); 0191 qreal step = 250; 0192 int numCycles = 20; 0193 0194 benchmarkWideArea(presetFileName, rect, step, numCycles, true, 0195 3000, 3000, 0, 0); 0196 } 0197 0198 void KisLowMemoryBenchmark::unlimitedMemoryHistoryPool50() 0199 { 0200 QString presetFileName = "autobrush_300px.kpp"; 0201 // one cycle takes about 48 MiB of memory (total 960 MiB) 0202 QRectF rect(150,150,4000,4000); 0203 qreal step = 250; 0204 int numCycles = 20; 0205 0206 benchmarkWideArea(presetFileName, rect, step, numCycles, true, 0207 3000, 3000, 50, 0); 0208 } 0209 0210 void KisLowMemoryBenchmark::memory2000History100Pool500HugeBrush() 0211 { 0212 QString presetFileName = "BIG_TESTING.kpp"; 0213 // one cycle takes about 316 MiB of memory (total 3+ GiB) 0214 QRectF rect(150,150,7850,7850); 0215 qreal step = 250; 0216 int numCycles = 10; 0217 0218 benchmarkWideArea(presetFileName, rect, step, numCycles, true, 0219 2000, 600, 500, 0); 0220 } 0221 0222 SIMPLE_TEST_MAIN(KisLowMemoryBenchmark)