diff --git a/libs/image/tiles3/KisTiledExtentManager.cpp b/libs/image/tiles3/KisTiledExtentManager.cpp index 9a71e0aa05..66985d0221 100644 --- a/libs/image/tiles3/KisTiledExtentManager.cpp +++ b/libs/image/tiles3/KisTiledExtentManager.cpp @@ -1,152 +1,155 @@ /* * Copyright (c) 2017 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KisTiledExtentManager.h" #include #include #include "kis_tile_data_interface.h" #include "kis_assert.h" #include "kis_global.h" +#include "kis_debug.h" +#include + namespace { inline bool addTileToMap(int index, QMap *map) { bool needsUpdateExtent = false; auto it = map->find(index); if (it == map->end()) { map->insert(index, 1); needsUpdateExtent = true; } else { KIS_ASSERT_RECOVER_NOOP(*it > 0); (*it)++; } return needsUpdateExtent; } inline bool removeTileFromMap(int index, QMap *map) { bool needsUpdateExtent = false; auto it = map->find(index); if (it == map->end()) { KIS_ASSERT_RECOVER_NOOP(0 && "sanity check failed: the tile has already been removed!"); } else { KIS_ASSERT_RECOVER_NOOP(*it > 0); (*it)--; if (*it <= 0) { map->erase(it); needsUpdateExtent = true; } } return needsUpdateExtent; } } KisTiledExtentManager::KisTiledExtentManager() { } void KisTiledExtentManager::notifyTileAdded(int col, int row) { QMutexLocker l(&m_mutex); bool needsUpdateExtent = false; - + qDebug() << Q_FUNC_INFO << ppVar(QThread::currentThreadId()) << ppVar(row) << ppVar(col); needsUpdateExtent |= addTileToMap(col, &m_colMap); needsUpdateExtent |= addTileToMap(row, &m_rowMap); if (needsUpdateExtent) { updateExtent(); } } void KisTiledExtentManager::notifyTileRemoved(int col, int row) { QMutexLocker l(&m_mutex); bool needsUpdateExtent = false; - + qDebug() << Q_FUNC_INFO << ppVar(QThread::currentThreadId()) << ppVar(row) << ppVar(col); needsUpdateExtent |= removeTileFromMap(col, &m_colMap); needsUpdateExtent |= removeTileFromMap(row, &m_rowMap); if (needsUpdateExtent) { updateExtent(); } } void KisTiledExtentManager::replaceTileStats(const QVector &indexes) { QMutexLocker l(&m_mutex); m_colMap.clear(); m_rowMap.clear(); Q_FOREACH (const QPoint &index, indexes) { addTileToMap(index.x(), &m_colMap); addTileToMap(index.y(), &m_rowMap); } updateExtent(); } void KisTiledExtentManager::clear() { QMutexLocker l(&m_mutex); m_colMap.clear(); m_rowMap.clear(); m_currentExtent = QRect(qint32_MAX, qint32_MAX, 0, 0); } QRect KisTiledExtentManager::extent() const { QMutexLocker l(&m_mutex); return m_currentExtent; } void KisTiledExtentManager::updateExtent() { KIS_ASSERT_RECOVER_RETURN(m_colMap.isEmpty() == m_rowMap.isEmpty()); // here we check for only one map for efficiency reasons if (m_colMap.isEmpty()) { m_currentExtent = QRect(qint32_MAX, qint32_MAX, 0, 0); } else { const int minX = m_colMap.firstKey() * KisTileData::WIDTH; const int maxPlusOneX = (m_colMap.lastKey() + 1) * KisTileData::WIDTH; const int minY = m_rowMap.firstKey() * KisTileData::HEIGHT; const int maxPlusOneY = (m_rowMap.lastKey() + 1) * KisTileData::HEIGHT; m_currentExtent = QRect(minX, minY, maxPlusOneX - minX, maxPlusOneY - minY); } } diff --git a/libs/image/tiles3/kis_tile_hash_table2.h b/libs/image/tiles3/kis_tile_hash_table2.h index 1cff78ba90..615a04b404 100644 --- a/libs/image/tiles3/kis_tile_hash_table2.h +++ b/libs/image/tiles3/kis_tile_hash_table2.h @@ -1,364 +1,385 @@ #ifndef KIS_TILEHASHTABLE_2_H #define KIS_TILEHASHTABLE_2_H #include "kis_shared.h" #include "kis_shared_ptr.h" #include "3rdparty/lock_free_map/concurrent_map.h" #include "kis_tile.h" +#include "kis_debug.h" + +#include template class KisTileHashTableTraits2 { static constexpr bool isInherited = std::is_convertible::value; Q_STATIC_ASSERT_X(isInherited, "Template must inherit KisShared"); public: typedef T TileType; typedef KisSharedPtr TileTypeSP; typedef KisWeakSharedPtr TileTypeWSP; KisTileHashTableTraits2(); KisTileHashTableTraits2(KisMementoManager *mm); KisTileHashTableTraits2(const KisTileHashTableTraits2 &ht, KisMementoManager *mm); ~KisTileHashTableTraits2(); - TileTypeSP insert(qint32 key, TileTypeSP value) + TileTypeSP insert(quint32 key, TileTypeSP value) { m_rawPointerUsers.fetchAndAddRelaxed(1); TileTypeSP::ref(&value, value.data()); TileType *result = m_map.assign(key, value.data()); if (result) { MemoryReclaimer *tmp = new MemoryReclaimer(result); QSBR::instance().enqueue(&MemoryReclaimer::destroy, tmp); } else { m_numTiles.fetchAndAddRelaxed(1); } TileTypeSP ptr(result); + qDebug() << Q_FUNC_INFO << ppVar(QThread::currentThreadId()) << ppVar(ptr.data()) << ppVar(key); m_rawPointerUsers.fetchAndSubRelaxed(1); return ptr; } - TileTypeSP erase(qint32 key) + TileTypeSP erase(quint32 key) { m_rawPointerUsers.fetchAndAddRelaxed(1); TileType *result = m_map.erase(key); TileTypeSP ptr(result); if (result) { m_numTiles.fetchAndSubRelaxed(1); MemoryReclaimer *tmp = new MemoryReclaimer(result); QSBR::instance().enqueue(&MemoryReclaimer::destroy, tmp); } if (m_rawPointerUsers == 1) { QSBR::instance().update(m_context); } + qDebug() << Q_FUNC_INFO << ppVar(QThread::currentThreadId()) << ppVar(ptr.data()) << ppVar(key); m_rawPointerUsers.fetchAndSubRelaxed(1); return ptr; } - TileTypeSP get(qint32 key) + TileTypeSP get(quint32 key) { m_rawPointerUsers.fetchAndAddRelaxed(1); TileTypeSP result(m_map.get(key)); + qDebug() << Q_FUNC_INFO << ppVar(QThread::currentThreadId()) << ppVar(result.data()) << ppVar(key); m_rawPointerUsers.fetchAndSubRelaxed(1); return result; } - TileTypeSP getLazy(qint32 key, TileTypeSP value, bool &newTile) + TileTypeSP getLazy(quint32 key, TileTypeSP value, bool &newTile) { m_rawPointerUsers.fetchAndAddRelaxed(1); newTile = false; - typename ConcurrentMap::Mutator iter = m_map.insertOrFind(key); + TileTypeSP tile(m_map.get(key)); - if (!iter.getValue()) { + if (!tile.data()) { TileTypeSP::ref(&value, value.data()); + TileType *result = m_map.assign(key, value.data()); - if (iter.exchangeValue(value.data()) == value.data()) { - TileTypeSP::deref(&value, value.data()); + if (result) { + MemoryReclaimer *tmp = new MemoryReclaimer(result); + QSBR::instance().enqueue(&MemoryReclaimer::destroy, tmp); } else { - m_numTiles.fetchAndAddRelaxed(1); newTile = true; + m_numTiles.fetchAndAddRelaxed(1); } + + tile = value; } - TileTypeSP result(iter.getValue()); + qDebug() << Q_FUNC_INFO << ppVar(QThread::currentThreadId()) << ppVar(tile.data()) << ppVar(key); m_rawPointerUsers.fetchAndSubRelaxed(1); - return result; + return tile; } bool isEmpty() { return !m_numTiles; } bool tileExists(qint32 col, qint32 row); /** * Returns a tile in position (col,row). If no tile exists, * returns null. * \param col column of the tile * \param row row of the tile */ TileTypeSP getExistingTile(qint32 col, qint32 row); /** * Returns a tile in position (col,row). If no tile exists, * creates a new one, attaches it to the list and returns. * \param col column of the tile * \param row row of the tile * \param newTile out-parameter, returns true if a new tile * was created */ TileTypeSP getTileLazy(qint32 col, qint32 row, bool& newTile); /** * Returns a tile in position (col,row). If no tile exists, * creates nothing, but returns shared default tile object * of the table. Be careful, this object has column and row * parameters set to (qint32_MIN, qint32_MIN). * \param col column of the tile * \param row row of the tile * \param existingTile returns true if the tile actually exists in the table * and it is not a lazily created default wrapper tile */ TileTypeSP getReadOnlyTileLazy(qint32 col, qint32 row, bool &existingTile); void addTile(TileTypeSP tile); bool deleteTile(TileTypeSP tile); bool deleteTile(qint32 col, qint32 row); void clear(); void setDefaultTileData(KisTileData *defaultTileData); KisTileData* defaultTileData() const; qint32 numTiles() { return m_numTiles; } void debugPrintInfo(); void debugMaxListLength(qint32 &min, qint32 &max); - typename ConcurrentMap::Iterator iterator() + typename ConcurrentMap::Iterator iterator() { - typename ConcurrentMap::Iterator iter(m_map); + typename ConcurrentMap::Iterator iter(m_map); return iter; } private: static inline quint32 calculateHash(qint32 col, qint32 row); struct MemoryReclaimer { MemoryReclaimer(TileType *data) : d(data) {} ~MemoryReclaimer() = default; void destroy() { TileTypeSP::deref(reinterpret_cast(this), d); this->MemoryReclaimer::~MemoryReclaimer(); delete this; } private: TileType *d; }; private: - ConcurrentMap m_map; + ConcurrentMap m_map; QSBR::Context m_context; QAtomicInt m_rawPointerUsers; QAtomicInt m_numTiles; KisTileData *m_defaultTileData; KisMementoManager *m_mementoManager; }; template class KisTileHashTableIteratorTraits2 { public: typedef T TileType; typedef KisSharedPtr TileTypeSP; KisTileHashTableIteratorTraits2(KisTileHashTableTraits2 *ht) : m_ht(ht) { } void next() { m_ht->iterator().next(); } TileTypeSP tile() const { return TileTypeSP(m_ht->iterator().getValue()); } bool isDone() const { return m_ht->iterator().isValid(); } void deleteCurrent() { m_ht->erase(m_ht->iterator().getKey()); next(); } void moveCurrentToHashTable(KisTileHashTableTraits2 *newHashTable) { TileTypeSP tile = m_ht->iterator().getValue(); next(); m_ht->deleteTile(tile); newHashTable->addTile(tile); } private: KisTileHashTableTraits2 *m_ht; }; template KisTileHashTableTraits2::KisTileHashTableTraits2() : m_context(QSBR::instance().createContext()), m_rawPointerUsers(0), m_numTiles(0), m_defaultTileData(0), m_mementoManager(0) { } template KisTileHashTableTraits2::KisTileHashTableTraits2(KisMementoManager *mm) : KisTileHashTableTraits2() { m_mementoManager = mm; } template KisTileHashTableTraits2::KisTileHashTableTraits2(const KisTileHashTableTraits2 &ht, KisMementoManager *mm) : KisTileHashTableTraits2(mm) { setDefaultTileData(ht.m_defaultTileData); - typename ConcurrentMap::Iterator iter(const_cast &>(ht.m_map)); + typename ConcurrentMap::Iterator iter(const_cast &>(ht.m_map)); while (iter.isValid()) { insert(iter.getKey(), iter.getValue()); iter.next(); } } template KisTileHashTableTraits2::~KisTileHashTableTraits2() { clear(); QSBR::instance().destroyContext(m_context); } template bool KisTileHashTableTraits2::tileExists(qint32 col, qint32 row) { return get(calculateHash(col, row)) != nullptr; } template typename KisTileHashTableTraits2::TileTypeSP KisTileHashTableTraits2::getExistingTile(qint32 col, qint32 row) { - return get(calculateHash(col, row)); + quint32 idx = calculateHash(col, row); + qDebug() << Q_FUNC_INFO << ppVar(QThread::currentThreadId()) << ppVar(row) << ppVar(col) << ppVar(idx); + return get(idx); } template typename KisTileHashTableTraits2::TileTypeSP KisTileHashTableTraits2::getTileLazy(qint32 col, qint32 row, bool &newTile) { TileTypeSP tile(new TileType(col, row, m_defaultTileData, m_mementoManager)); - return getLazy(calculateHash(col, row), tile, newTile); + quint32 idx = calculateHash(col, row); + qDebug() << Q_FUNC_INFO << ppVar(QThread::currentThreadId()) << ppVar(row) << ppVar(col) << ppVar(idx); + return getLazy(idx, tile, newTile); } template typename KisTileHashTableTraits2::TileTypeSP KisTileHashTableTraits2::getReadOnlyTileLazy(qint32 col, qint32 row, bool &existingTile) { m_rawPointerUsers.fetchAndAddRelaxed(1); TileTypeSP tile(m_map.get(calculateHash(col, row))); existingTile = tile; if (!existingTile) { tile = new TileType(col, row, m_defaultTileData, 0); } m_rawPointerUsers.fetchAndSubRelaxed(1); return tile; } template -void KisTileHashTableTraits2::addTile(TileTypeSP value) +void KisTileHashTableTraits2::addTile(TileTypeSP tile) { - insert(m_numTiles.load() + 1, value); + quint32 idx = calculateHash(tile->col(), tile->row()); + qDebug() << Q_FUNC_INFO << ppVar(QThread::currentThreadId()) << ppVar(tile->row()) << ppVar(tile->col()) << ppVar(idx); + insert(idx, tile); } template bool KisTileHashTableTraits2::deleteTile(TileTypeSP tile) { return deleteTile(tile->col(), tile->row()); } template bool KisTileHashTableTraits2::deleteTile(qint32 col, qint32 row) { - return erase(calculateHash(col, row)) != nullptr; + quint32 idx = calculateHash(col, row); + qDebug() << Q_FUNC_INFO << ppVar(QThread::currentThreadId()) << ppVar(row) << ppVar(col) << ppVar(idx); + return erase(idx) != nullptr; } template void KisTileHashTableTraits2::clear() { - typename ConcurrentMap::Iterator iter(m_map); + typename ConcurrentMap::Iterator iter(m_map); while (iter.isValid()) { erase(iter.getKey()); iter.next(); } } template inline void KisTileHashTableTraits2::setDefaultTileData(KisTileData *defaultTileData) { m_rawPointerUsers.fetchAndAddRelaxed(1); if (m_defaultTileData) { m_defaultTileData->release(); m_defaultTileData = 0; } if (defaultTileData) { defaultTileData->acquire(); m_defaultTileData = defaultTileData; } m_rawPointerUsers.fetchAndSubRelaxed(1); } template inline KisTileData* KisTileHashTableTraits2::defaultTileData() const { return m_defaultTileData; } template void KisTileHashTableTraits2::debugPrintInfo() { } template void KisTileHashTableTraits2::debugMaxListLength(qint32 &min, qint32 &max) { } template quint32 KisTileHashTableTraits2::calculateHash(qint32 col, qint32 row) { - return ((row << 5) + (col & 0x1F)) & 0x3FF; + std::size_t seed = 0; + boost::hash_combine(seed, col); + boost::hash_combine(seed, row); + return seed; } typedef KisTileHashTableTraits2 KisTileHashTable; typedef KisTileHashTableIteratorTraits2 KisTileHashTableIterator; typedef KisTileHashTableIteratorTraits2 KisTileHashTableConstIterator; #endif // KIS_TILEHASHTABLE_2_H diff --git a/libs/ui/tests/FreehandStrokeBenchmark.cpp b/libs/ui/tests/FreehandStrokeBenchmark.cpp index 88ffcee1e3..d3a3d93855 100644 --- a/libs/ui/tests/FreehandStrokeBenchmark.cpp +++ b/libs/ui/tests/FreehandStrokeBenchmark.cpp @@ -1,147 +1,147 @@ /* * Copyright (c) 2017 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "FreehandStrokeBenchmark.h" #include #include #include #include "stroke_testing_utils.h" #include "strokes/freehand_stroke.h" #include "strokes/KisFreehandStrokeInfo.h" #include "kis_resources_snapshot.h" #include "kis_image.h" #include class FreehandStrokeBenchmarkTester : public utils::StrokeTester { public: FreehandStrokeBenchmarkTester(const QString &presetFilename) : StrokeTester("freehand_benchmark", QSize(5000, 5000), presetFilename) { setBaseFuzziness(3); } void setCpuCoresLimit(int value) { m_cpuCoresLimit = value; } protected: using utils::StrokeTester::initImage; void initImage(KisImageWSP image, KisNodeSP activeNode) override { Q_UNUSED(activeNode); if (m_cpuCoresLimit > 0) { image->setWorkingThreadsLimit(m_cpuCoresLimit); } } KisStrokeStrategy* createStroke(KisResourcesSnapshotSP resources, KisImageWSP image) override { Q_UNUSED(image); KisFreehandStrokeInfo *strokeInfo = new KisFreehandStrokeInfo(); QScopedPointer stroke( new FreehandStrokeStrategy(resources, strokeInfo, kundo2_noi18n("Freehand Stroke"))); return stroke.take(); } void addPaintingJobs(KisImageWSP image, KisResourcesSnapshotSP resources) override { addPaintingJobs(image, resources, 0); } void addPaintingJobs(KisImageWSP image, KisResourcesSnapshotSP resources, int iteration) override { Q_UNUSED(iteration); Q_UNUSED(resources); for (int y = 100; y < 4900; y += 300) { KisPaintInformation pi1; KisPaintInformation pi2; pi1 = KisPaintInformation(QPointF(100, y), 0.5); pi2 = KisPaintInformation(QPointF(4900, y + 100), 1.0); QScopedPointer data( new FreehandStrokeStrategy::Data(0, pi1, pi2)); image->addJob(strokeId(), data.take()); } image->addJob(strokeId(), new FreehandStrokeStrategy::UpdateData(true)); } private: KisFreehandStrokeInfo *m_strokeInfo; int m_cpuCoresLimit = -1; }; void benchmarkBrush(const QString &presetName) { FreehandStrokeBenchmarkTester tester(presetName); // for (int i = 1; i <= QThread::idealThreadCount(); i++) { // tester.setCpuCoresLimit(i); // tester.benchmark(); // qDebug() << qPrintable(QString("Cores: %1 Time: %2 (ms)").arg(i).arg(tester.lastStrokeTime())); // } qint32 threadCount = QThread::idealThreadCount(); tester.setCpuCoresLimit(threadCount); tester.benchmark(); qDebug() << qPrintable(QString("Cores: %1 Time: %2 (ms)").arg(threadCount).arg(tester.lastStrokeTime())); } #include void FreehandStrokeBenchmark::initTestCase() { KoResourcePaths::addResourceType("kis_brushes", "data", FILES_DATA_DIR); } void FreehandStrokeBenchmark::testDefaultTip() { -// benchmarkBrush("testing_1000px_auto_deafult.kpp"); + benchmarkBrush("testing_1000px_auto_deafult.kpp"); } void FreehandStrokeBenchmark::testSoftTip() { // benchmarkBrush("testing_1000px_auto_soft.kpp"); } void FreehandStrokeBenchmark::testGaussianTip() { - benchmarkBrush("testing_1000px_auto_gaussian.kpp"); +// benchmarkBrush("testing_1000px_auto_gaussian.kpp"); } void FreehandStrokeBenchmark::testStampTip() { // benchmarkBrush("testing_1000px_stamp_450_rotated.kpp"); } void FreehandStrokeBenchmark::testColorsmudgeDefaultTip() { // benchmarkBrush("testing_200px_colorsmudge_default.kpp"); } QTEST_MAIN(FreehandStrokeBenchmark)