diff --git a/libs/ui/CMakeLists.txt b/libs/ui/CMakeLists.txt --- a/libs/ui/CMakeLists.txt +++ b/libs/ui/CMakeLists.txt @@ -160,6 +160,7 @@ opengl/kis_opengl_image_textures.cpp opengl/kis_texture_tile.cpp opengl/kis_opengl_shader_loader.cpp + opengl/kis_texture_tile_info_pool.cpp kis_fps_decoration.cpp recorder/kis_node_query_path_editor.cc recorder/kis_recorded_action_creator.cc diff --git a/libs/ui/opengl/kis_texture_tile_info_pool.h b/libs/ui/opengl/kis_texture_tile_info_pool.h --- a/libs/ui/opengl/kis_texture_tile_info_pool.h +++ b/libs/ui/opengl/kis_texture_tile_info_pool.h @@ -26,10 +26,12 @@ #include #include #include +#include #include "kis_assert.h" #include "kis_debug.h" #include "kis_global.h" +#include "kis_signal_compressor.h" const int minPoolChunk = 32; // 8 MiB (default, with tilesize 256) const int maxPoolChunk = 128; // 32 MiB (default, with tilesize 256) @@ -54,7 +56,8 @@ : m_chunkSize(tileWidth * tileHeight * pixelSize), m_pool(m_chunkSize, minPoolChunk, maxPoolChunk), m_numAllocations(0), - m_maxAllocations(0) + m_maxAllocations(0), + m_numFrees(0) { } @@ -65,45 +68,79 @@ return (quint8*)m_pool.malloc(); } - void free(quint8 *ptr) { + bool free(quint8 *ptr) { m_numAllocations--; + m_numFrees++; m_pool.free(ptr); KIS_ASSERT_RECOVER_NOOP(m_numAllocations >= 0); - if (!m_numAllocations && m_maxAllocations > freeThreshold) { - // qDebug() << "Purging memory" << ppVar(m_maxAllocations); - m_pool.purge_memory(); - m_maxAllocations = 0; - } + return !m_numAllocations && m_maxAllocations > freeThreshold; } int chunkSize() const { return m_chunkSize; } + int numFrees() const { + return m_numFrees; + } + + void tryPurge(int numFrees) { + // checking numFrees here is asserting that there were no frees + // between the time we originally indicated the purge and now. + if (numFrees == m_numFrees && !m_numAllocations) { + m_pool.purge_memory(); + m_maxAllocations = 0; + } + } + private: const int m_chunkSize; boost::pool m_pool; int m_numAllocations; int m_maxAllocations; + int m_numFrees; +}; + +class KisTextureTileInfoPool; + +class KisTextureTileInfoPoolWorker : public QObject +{ + Q_OBJECT +public: + KisTextureTileInfoPoolWorker(KisTextureTileInfoPool *pool); + +public Q_SLOTS: + void slotPurge(int pixelSize, int numFrees); + void slotDelayedPurge(); + +private: + KisTextureTileInfoPool *m_pool; + KisSignalCompressor m_compressor; + QMap m_purge; }; /** * A universal pool for keeping the openGL tile of different pixel * sizes. The underlying pools are created for each pixel size on * demand. */ -class KisTextureTileInfoPool +class KisTextureTileInfoPool : public QObject { + Q_OBJECT public: KisTextureTileInfoPool(int tileWidth, int tileHeight) : m_tileWidth(tileWidth), m_tileHeight(tileHeight) { + m_worker = new KisTextureTileInfoPoolWorker(this); + m_worker->moveToThread(QApplication::instance()->thread()); + connect(this, SIGNAL(purge(int, int)), m_worker, SLOT(slotPurge(int, int))); } ~KisTextureTileInfoPool() { + delete m_worker; qDeleteAll(m_pools); } @@ -130,7 +167,10 @@ */ void free(quint8 *ptr, int pixelSize) { QMutexLocker l(&m_mutex); - m_pools[pixelSize]->free(ptr); + KisTextureTileInfoPoolSingleSize *pool = m_pools[pixelSize]; + if (pool->free(ptr)) { + emit purge(pixelSize, pool->numFrees()); + } } /** @@ -141,11 +181,20 @@ return m_pools[pixelSize]->chunkSize(); } + void tryPurge(int pixelSize, int numFrees) { + QMutexLocker l(&m_mutex); + m_pools[pixelSize]->tryPurge(numFrees); + } + +Q_SIGNALS: + void purge(int pixelSize, int numFrees); + private: mutable QMutex m_mutex; const int m_tileWidth; const int m_tileHeight; QVector m_pools; + KisTextureTileInfoPoolWorker *m_worker; }; typedef QSharedPointer KisTextureTileInfoPoolSP; diff --git a/libs/ui/opengl/kis_texture_tile_info_pool.cpp b/libs/ui/opengl/kis_texture_tile_info_pool.cpp new file mode 100644 --- /dev/null +++ b/libs/ui/opengl/kis_texture_tile_info_pool.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2017 Bernhard Liebl + * + * 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 "kis_texture_tile_info_pool.h" + +KisTextureTileInfoPoolWorker::KisTextureTileInfoPoolWorker(KisTextureTileInfoPool *pool) + : m_pool(pool) + , m_compressor(1000, KisSignalCompressor::POSTPONE) +{ + connect(&m_compressor, SIGNAL(timeout()), this, SLOT(slotDelayedPurge())); +} + +void KisTextureTileInfoPoolWorker::slotPurge(int pixelSize, int numFrees) +{ + m_purge[pixelSize] = numFrees; + m_compressor.start(); +} + +void KisTextureTileInfoPoolWorker::slotDelayedPurge() +{ + for (QMap::const_iterator i = m_purge.constBegin(); i != m_purge.constEnd(); i++) { + m_pool->tryPurge(i.key(), i.value()); + } + + m_purge.clear(); +}