File indexing completed on 2024-12-22 04:12:47

0001 /*
0002  *  SPDX-FileCopyrightText: 2016 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #ifndef __KIS_TEXTURE_TILE_INFO_POOL_H
0008 #define __KIS_TEXTURE_TILE_INFO_POOL_H
0009 
0010 #include <boost/pool/pool.hpp>
0011 #include <QtGlobal>
0012 #include <QVector>
0013 
0014 #include <QMutex>
0015 #include <QMutexLocker>
0016 #include <QSharedPointer>
0017 #include <QApplication>
0018 
0019 #include "kis_assert.h"
0020 #include "kis_debug.h"
0021 #include "kis_global.h"
0022 #include "kis_signal_compressor.h"
0023 
0024 #include "kritaui_export.h"
0025 
0026 const int minPoolChunk = 32; // 8 MiB (default, with tilesize 256)
0027 const int maxPoolChunk = 128; // 32 MiB (default, with tilesize 256)
0028 const int freeThreshold = 64; // 16 MiB (default, with tilesize 256)
0029 
0030 
0031 /**
0032  * A pool for keeping the chunks of data of constant size. We have one
0033  * such pool per used openGL tile size. The size of the chunk
0034  * obviously depends on the size of the tile in pixels and the size of
0035  * a single pixel in bytes.
0036  *
0037  * As soon as the number of allocations drops to zero, all the memory
0038  * is returned back to the operating system. Please note, that there
0039  * is *no way* of reclaiming even unused pool memory until *all* the
0040  * allocated chunks are free'd.
0041  */
0042 class KRITAUI_EXPORT KisTextureTileInfoPoolSingleSize
0043 {
0044 public:
0045     KisTextureTileInfoPoolSingleSize(int tileWidth, int tileHeight, int pixelSize)
0046         : m_chunkSize(tileWidth * tileHeight * pixelSize),
0047           m_pool(m_chunkSize, minPoolChunk, maxPoolChunk),
0048           m_numAllocations(0),
0049           m_maxAllocations(0),
0050           m_numFrees(0)
0051     {
0052     }
0053 
0054     quint8* malloc() {
0055         m_numAllocations++;
0056         m_maxAllocations = qMax(m_maxAllocations, m_numAllocations);
0057 
0058         return (quint8*)m_pool.malloc();
0059     }
0060 
0061     bool free(quint8 *ptr) {
0062         m_numAllocations--;
0063         m_numFrees++;
0064         m_pool.free(ptr);
0065 
0066         KIS_ASSERT_RECOVER_NOOP(m_numAllocations >= 0);
0067 
0068         return !m_numAllocations && m_maxAllocations > freeThreshold;
0069     }
0070 
0071     int chunkSize() const {
0072         return m_chunkSize;
0073     }
0074 
0075     int numFrees() const {
0076         return m_numFrees;
0077     }
0078 
0079     void tryPurge(int numFrees) {
0080         // checking numFrees here is asserting that there were no frees
0081         // between the time we originally indicated the purge and now.
0082         if (numFrees == m_numFrees && !m_numAllocations) {
0083             m_pool.purge_memory();
0084             m_maxAllocations = 0;
0085         }
0086     }
0087 
0088 private:
0089     const int m_chunkSize;
0090     boost::pool<boost::default_user_allocator_new_delete> m_pool;
0091     int m_numAllocations;
0092     int m_maxAllocations;
0093     int m_numFrees;
0094 };
0095 
0096 class KisTextureTileInfoPool;
0097 
0098 class KRITAUI_EXPORT KisTextureTileInfoPoolWorker : public QObject
0099 {
0100     Q_OBJECT
0101 public:
0102     KisTextureTileInfoPoolWorker(KisTextureTileInfoPool *pool);
0103 
0104 public Q_SLOTS:
0105     void slotPurge(int pixelSize, int numFrees);
0106     void slotDelayedPurge();
0107 
0108 private:
0109     KisTextureTileInfoPool *m_pool;
0110     KisSignalCompressor m_compressor;
0111     QMap<int, int> m_purge;
0112 };
0113 
0114 /**
0115  * A universal pool for keeping the openGL tile of different pixel
0116  * sizes.  The underlying pools are created for each pixel size on
0117  * demand.
0118  */
0119 class KRITAUI_EXPORT KisTextureTileInfoPool : public QObject
0120 {
0121     Q_OBJECT
0122 public:
0123     KisTextureTileInfoPool(int tileWidth, int tileHeight)
0124         : m_tileWidth(tileWidth),
0125           m_tileHeight(tileHeight)
0126     {
0127         m_worker = new KisTextureTileInfoPoolWorker(this);
0128         m_worker->moveToThread(QApplication::instance()->thread());
0129         connect(this, SIGNAL(purge(int, int)), m_worker, SLOT(slotPurge(int, int)));
0130     }
0131 
0132     ~KisTextureTileInfoPool() {
0133         delete m_worker;
0134         qDeleteAll(m_pools);
0135     }
0136 
0137     /**
0138      * Alloc a tile with the specified pixel size
0139      */
0140     quint8* malloc(int pixelSize) {
0141         QMutexLocker l(&m_mutex);
0142 
0143         if (m_pools.size() <= pixelSize) {
0144             m_pools.resize(pixelSize + 1);
0145         }
0146 
0147         if (!m_pools[pixelSize]) {
0148             m_pools[pixelSize] =
0149                 new KisTextureTileInfoPoolSingleSize(m_tileWidth, m_tileHeight, pixelSize);
0150         }
0151 
0152         return m_pools[pixelSize]->malloc();
0153     }
0154 
0155     /**
0156      * Free a tile with the specified pixel size
0157      */
0158     void free(quint8 *ptr, int pixelSize) {
0159         QMutexLocker l(&m_mutex);
0160         KisTextureTileInfoPoolSingleSize *pool = m_pools[pixelSize];
0161         if (pool->free(ptr)) {
0162             emit purge(pixelSize, pool->numFrees());
0163         }
0164     }
0165 
0166     /**
0167      * \return the length of the chunks stored in the pool
0168      */
0169     int chunkSize(int pixelSize) const {
0170         QMutexLocker l(&m_mutex);
0171         return m_pools[pixelSize]->chunkSize();
0172     }
0173 
0174     void tryPurge(int pixelSize, int numFrees) {
0175         QMutexLocker l(&m_mutex);
0176         m_pools[pixelSize]->tryPurge(numFrees);
0177     }
0178 
0179 Q_SIGNALS:
0180     void purge(int pixelSize, int numFrees);
0181 
0182 private:
0183     mutable QMutex m_mutex;
0184     const int m_tileWidth;
0185     const int m_tileHeight;
0186     QVector<KisTextureTileInfoPoolSingleSize*> m_pools;
0187     KisTextureTileInfoPoolWorker *m_worker;
0188 };
0189 
0190 typedef QSharedPointer<KisTextureTileInfoPool> KisTextureTileInfoPoolSP;
0191 
0192 class KRITAUI_EXPORT KisTextureTileInfoPoolRegistry
0193 {
0194     typedef QWeakPointer<KisTextureTileInfoPool> KisTextureTileInfoPoolWSP;
0195     typedef QPair<int, int> PoolId;
0196 
0197 public:
0198     KisTextureTileInfoPoolSP getPool(int tileWidth, int tileHeight) {
0199         QMutexLocker l(&m_mutex);
0200 
0201         PoolId id(tileWidth, tileHeight);
0202 
0203         KisTextureTileInfoPoolSP pool =
0204             m_storage[id].toStrongRef();
0205 
0206         if (!pool) {
0207             pool = toQShared(
0208                 new KisTextureTileInfoPool(tileWidth, tileHeight));
0209             m_storage[id] = pool;
0210         }
0211 
0212         return pool;
0213     }
0214 
0215 private:
0216     QMutex m_mutex;
0217     QHash<PoolId, KisTextureTileInfoPoolWSP> m_storage;
0218 };
0219 
0220 
0221 #endif /* __KIS_TEXTURE_TILE_INFO_POOL_H */