1 /*
2 * Copyright (c) 2012 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19 #include "kis_low_memory_benchmark.h"
20
21 #include <QTest>
22
23 #include "kis_benchmark_values.h"
24
25 #include <KoColor.h>
26 #include <KoColorSpace.h>
27 #include <KoColorSpaceRegistry.h>
28
29 #include <kis_image.h>
30 #include <kis_layer.h>
31 #include <kis_paint_layer.h>
32 #include "kis_paint_device.h"
33 #include "kis_painter.h"
34
35 #include <brushengine/kis_paint_information.h>
36 #include <brushengine/kis_paintop_registry.h>
37 #include <brushengine/kis_paintop_preset.h>
38
39 #include "tiles3/kis_tile_data_store.h"
40 #include "kis_surrogate_undo_adapter.h"
41 #include "kis_image_config.h"
42 #define LOAD_PRESET_OR_RETURN(preset, fileName) \
43 if(!preset->load()) { dbgKrita << "Preset" << fileName << "was NOT loaded properly. Done."; return; } \
44 else dbgKrita << "Loaded preset:" << fileName
45
46 #define HUGE_IMAGE_SIZE 8000
47
48 /**
49 * This benchmark runs a series of huge strokes on a canvas with a
50 * particular configuration of the swapper/pooler and history
51 * management. After the test is done you can visualize the results
52 * with the GNU Octave. Please use kis_low_memory_show_report.m file
53 * for that.
54 */
benchmarkWideArea(const QString presetFileName,const QRectF & rect,qreal vstep,int numCycles,bool createTransaction,int hardLimitMiB,int softLimitMiB,int poolLimitMiB,int index)55 void KisLowMemoryBenchmark::benchmarkWideArea(const QString presetFileName,
56 const QRectF &rect, qreal vstep,
57 int numCycles,
58 bool createTransaction,
59 int hardLimitMiB,
60 int softLimitMiB,
61 int poolLimitMiB,
62 int index)
63 {
64 KisPaintOpPresetSP preset = new KisPaintOpPreset(QString(FILES_DATA_DIR) + '/' + presetFileName);
65 LOAD_PRESET_OR_RETURN(preset, presetFileName);
66
67
68 /**
69 * Initialize image and painter
70 */
71 const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->rgb8();
72 KisImageSP image = new KisImage(0, HUGE_IMAGE_SIZE, HUGE_IMAGE_SIZE, colorSpace, "stroke sample image");
73 KisLayerSP layer = new KisPaintLayer(image, "temporary for stroke sample", OPACITY_OPAQUE_U8, colorSpace);
74 KisLayerSP layerExtra = new KisPaintLayer(image, "temporary for threading", OPACITY_OPAQUE_U8, colorSpace);
75
76 image->addNode(layer, image->root());
77 image->addNode(layerExtra, image->root());
78
79 KisPainter *painter = new KisPainter(layer->paintDevice());
80
81 painter->setPaintColor(KoColor(Qt::black, colorSpace));
82 painter->setPaintOpPreset(preset, layer, image);
83
84 /**
85 * A simple adapter that will store all the transactions for us
86 */
87 KisSurrogateUndoAdapter undoAdapter;
88
89 /**
90 * Reset configuration to the desired settings
91 */
92 KisImageConfig config(false);
93 qreal oldHardLimit = config.memoryHardLimitPercent();
94 qreal oldSoftLimit = config.memorySoftLimitPercent();
95 qreal oldPoolLimit = config.memoryPoolLimitPercent();
96 const qreal _MiB = 100.0 / KisImageConfig::totalRAM();
97
98 config.setMemoryHardLimitPercent(hardLimitMiB * _MiB);
99 config.setMemorySoftLimitPercent(softLimitMiB * _MiB);
100 config.setMemoryPoolLimitPercent(poolLimitMiB * _MiB);
101
102 KisTileDataStore::instance()->testingRereadConfig();
103
104 /**
105 * Create an empty the log file
106 */
107 QString fileName;
108 fileName = QString("log_%1_%2_%3_%4_%5.txt")
109 .arg(createTransaction)
110 .arg(hardLimitMiB)
111 .arg(softLimitMiB)
112 .arg(poolLimitMiB)
113 .arg(index);
114
115 QFile logFile(fileName);
116 logFile.open(QFile::WriteOnly | QFile::Truncate);
117 QTextStream logStream(&logFile);
118 logStream.setFieldWidth(10);
119 logStream.setFieldAlignment(QTextStream::AlignRight);
120
121 /**
122 * Start painting on the image
123 */
124
125 QTime cycleTime;
126 QTime lineTime;
127 cycleTime.start();
128 lineTime.start();
129
130 qreal rectBottom = rect.y() + rect.height();
131
132 for (int i = 0; i < numCycles; i++) {
133 cycleTime.restart();
134
135 QLineF line(rect.topLeft(), rect.topLeft() + QPointF(rect.width(), 0));
136 if (createTransaction) {
137 painter->beginTransaction();
138 }
139
140 KisDistanceInformation currentDistance;
141
142 while(line.y1() < rectBottom) {
143 lineTime.restart();
144
145 KisPaintInformation pi1(line.p1(), 0.0);
146 KisPaintInformation pi2(line.p2(), 1.0);
147 painter->paintLine(pi1, pi2, ¤tDistance);
148 painter->device()->setDirty(painter->takeDirtyRegion());
149
150 logStream << "L 1" << i << lineTime.elapsed()
151 << KisTileDataStore::instance()->numTilesInMemory() * 16
152 << KisTileDataStore::instance()->numTiles() * 16
153 << createTransaction << endl;
154
155 line.translate(0, vstep);
156 }
157
158 painter->device()->setDirty(painter->takeDirtyRegion());
159
160 if (createTransaction) {
161 painter->endTransaction(&undoAdapter);
162 }
163
164 // comment/uncomment to emulate user waiting after the stroke
165 QTest::qSleep(1000);
166
167 logStream << "C 2" << i << cycleTime.elapsed()
168 << KisTileDataStore::instance()->numTilesInMemory() * 16
169 << KisTileDataStore::instance()->numTiles() * 16
170 << createTransaction
171 << config.memoryHardLimitPercent() / _MiB
172 << config.memorySoftLimitPercent() / _MiB
173 << config.memoryPoolLimitPercent() / _MiB << endl;
174 }
175
176 config.setMemoryHardLimitPercent(oldHardLimit * _MiB);
177 config.setMemorySoftLimitPercent(oldSoftLimit * _MiB);
178 config.setMemoryPoolLimitPercent(oldPoolLimit * _MiB);
179
180 delete painter;
181 }
182
unlimitedMemoryNoHistoryNoPool()183 void KisLowMemoryBenchmark::unlimitedMemoryNoHistoryNoPool()
184 {
185 QString presetFileName = "autobrush_300px.kpp";
186 // one cycle takes about 48 MiB of memory (total 960 MiB)
187 QRectF rect(150,150,4000,4000);
188 qreal step = 250;
189 int numCycles = 20;
190
191 benchmarkWideArea(presetFileName, rect, step, numCycles, false,
192 3000, 3000, 0, 0);
193 }
194
unlimitedMemoryHistoryNoPool()195 void KisLowMemoryBenchmark::unlimitedMemoryHistoryNoPool()
196 {
197 QString presetFileName = "autobrush_300px.kpp";
198 // one cycle takes about 48 MiB of memory (total 960 MiB)
199 QRectF rect(150,150,4000,4000);
200 qreal step = 250;
201 int numCycles = 20;
202
203 benchmarkWideArea(presetFileName, rect, step, numCycles, true,
204 3000, 3000, 0, 0);
205 }
206
unlimitedMemoryHistoryPool50()207 void KisLowMemoryBenchmark::unlimitedMemoryHistoryPool50()
208 {
209 QString presetFileName = "autobrush_300px.kpp";
210 // one cycle takes about 48 MiB of memory (total 960 MiB)
211 QRectF rect(150,150,4000,4000);
212 qreal step = 250;
213 int numCycles = 20;
214
215 benchmarkWideArea(presetFileName, rect, step, numCycles, true,
216 3000, 3000, 50, 0);
217 }
218
memory2000History100Pool500HugeBrush()219 void KisLowMemoryBenchmark::memory2000History100Pool500HugeBrush()
220 {
221 QString presetFileName = "BIG_TESTING.kpp";
222 // one cycle takes about 316 MiB of memory (total 3+ GiB)
223 QRectF rect(150,150,7850,7850);
224 qreal step = 250;
225 int numCycles = 10;
226
227 benchmarkWideArea(presetFileName, rect, step, numCycles, true,
228 2000, 600, 500, 0);
229 }
230
231 QTEST_MAIN(KisLowMemoryBenchmark)
232