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, &currentDistance);
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)