File indexing completed on 2024-05-19 04:29:12

0001 /*
0002  *  SPDX-FileCopyrightText: 2018 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 #include "KisFrameCacheStore.h"
0007 
0008 #include <KoColorSpace.h>
0009 #include "kis_update_info.h"
0010 #include "KisFrameDataSerializer.h"
0011 #include "opengl/KisOpenGLUpdateInfoBuilder.h"
0012 
0013 #define SANITY_CHECK
0014 
0015 namespace {
0016 enum FrameType {
0017     FrameFull,
0018     FrameCopy,
0019     FrameDiff
0020 };
0021 
0022 struct FrameInfo;
0023 typedef QSharedPointer<FrameInfo> FrameInfoSP;
0024 
0025 struct FrameInfo {
0026     // full frame
0027     FrameInfo(const QRect &dirtyImageRect, const QRect &imageBounds, int levelOfDetail, KisFrameDataSerializer &serializer, const KisFrameDataSerializer::Frame &frame);
0028     // diff frame
0029     FrameInfo(const QRect &dirtyImageRect, const QRect &imageBounds, int levelOfDetail, KisFrameDataSerializer &serializer, FrameInfoSP baseFrame, const KisFrameDataSerializer::Frame &frame);
0030     // copy frame
0031     FrameInfo(const QRect &dirtyImageRect, const QRect &imageBounds, int levelOfDetail, KisFrameDataSerializer &serializer, FrameInfoSP baseFrame);
0032 
0033     ~FrameInfo();
0034 
0035     FrameType type() const {
0036         return m_type;
0037     }
0038 
0039     int levelOfDetail() const {
0040         return m_levelOfDetail;
0041     }
0042 
0043     QRect dirtyImageRect() const {
0044         return m_dirtyImageRect;
0045     }
0046 
0047     QRect imageBounds() const {
0048         return m_imageBounds;
0049     }
0050 
0051     int frameDataId() const {
0052         return m_savedFrameDataId;
0053     }
0054 
0055     FrameInfoSP baseFrame() const {
0056         return m_baseFrame;
0057     }
0058 
0059     int m_levelOfDetail = 0;
0060     QRect m_dirtyImageRect;
0061     QRect m_imageBounds;
0062     FrameInfoSP m_baseFrame;
0063     FrameType m_type = FrameFull;
0064     int m_savedFrameDataId = -1;
0065     KisFrameDataSerializer &m_serializer;
0066 };
0067 
0068 // full frame
0069 FrameInfo::FrameInfo(const QRect &dirtyImageRect, const QRect &imageBounds, int levelOfDetail, KisFrameDataSerializer &serializer, const KisFrameDataSerializer::Frame &frame)
0070     : m_levelOfDetail(levelOfDetail),
0071       m_dirtyImageRect(dirtyImageRect),
0072       m_imageBounds(imageBounds),
0073       m_baseFrame(0),
0074       m_type(FrameFull),
0075       m_serializer(serializer)
0076 {
0077     m_savedFrameDataId = m_serializer.saveFrame(frame);
0078 }
0079 
0080 // diff frame
0081 FrameInfo::FrameInfo(const QRect &dirtyImageRect, const QRect &imageBounds, int levelOfDetail, KisFrameDataSerializer &serializer, FrameInfoSP baseFrame, const KisFrameDataSerializer::Frame &frame)
0082     : m_levelOfDetail(levelOfDetail),
0083       m_dirtyImageRect(dirtyImageRect),
0084       m_imageBounds(imageBounds),
0085       m_baseFrame(baseFrame),
0086       m_type(FrameDiff),
0087       m_serializer(serializer)
0088 {
0089     m_savedFrameDataId = m_serializer.saveFrame(frame);
0090 }
0091 
0092 // copy frame
0093 FrameInfo::FrameInfo(const QRect &dirtyImageRect, const QRect &imageBounds, int levelOfDetail, KisFrameDataSerializer &serializer, FrameInfoSP baseFrame)
0094     : m_levelOfDetail(levelOfDetail),
0095       m_dirtyImageRect(dirtyImageRect),
0096       m_imageBounds(imageBounds),
0097       m_baseFrame(baseFrame),
0098       m_type(FrameCopy),
0099       m_savedFrameDataId(-1),
0100       m_serializer(serializer)
0101 {
0102 }
0103 
0104 FrameInfo::~FrameInfo()
0105 {
0106     KIS_SAFE_ASSERT_RECOVER_RETURN(m_savedFrameDataId >= 0 || m_type == FrameCopy);
0107 
0108     if (m_savedFrameDataId >= 0) {
0109         m_serializer.forgetFrame(m_savedFrameDataId);
0110     }
0111 }
0112 
0113 }
0114 
0115 
0116 struct KRITAUI_NO_EXPORT KisFrameCacheStore::Private
0117 {
0118     Private(const QString &frameCachePath)
0119         : serializer(frameCachePath)
0120     {
0121     }
0122 
0123     // the serializer should be killed after *all* the frame info objects
0124     // got destroyed, because they use it in their own destruction
0125     KisFrameDataSerializer serializer;
0126 
0127     KisFrameDataSerializer::Frame lastSavedFullFrame;
0128     int lastSavedFullFrameId = -1;
0129 
0130     KisFrameDataSerializer::Frame lastLoadedBaseFrame;
0131     FrameInfoSP lastLoadedBaseFrameInfo;
0132 
0133     QMap<int, FrameInfoSP> savedFrames;
0134 };
0135 
0136 KisFrameCacheStore::KisFrameCacheStore()
0137     : KisFrameCacheStore(QString())
0138 {
0139 }
0140 
0141 KisFrameCacheStore::KisFrameCacheStore(const QString &frameCachePath)
0142     : m_d(new Private(frameCachePath))
0143 {
0144 }
0145 
0146 
0147 KisFrameCacheStore::~KisFrameCacheStore()
0148 {
0149 }
0150 
0151 void KisFrameCacheStore::saveFrame(int frameId, KisOpenGLUpdateInfoSP info, const QRect &imageBounds)
0152 {
0153     int pixelSize = 0;
0154 
0155     Q_FOREACH (auto tile, info->tileList) {
0156 #ifdef SANITY_CHECK
0157         if (!pixelSize) {
0158             pixelSize = tile->pixelSize();
0159         } else {
0160             KIS_SAFE_ASSERT_RECOVER_RETURN(pixelSize == tile->pixelSize());
0161         }
0162 #else
0163         pixelSize = tile->pixelSize();
0164         break;
0165 #endif
0166     }
0167 
0168     KIS_SAFE_ASSERT_RECOVER_RETURN(pixelSize);
0169 
0170     // TODO: assert that dirty image rect is equal to the full image rect
0171     // TODO: assert tile color space coincides with the destination color space
0172 
0173     KisFrameDataSerializer::Frame frame;
0174     frame.pixelSize = pixelSize;
0175 
0176     for (auto it = info->tileList.begin(); it != info->tileList.end(); ++it) {
0177         KisFrameDataSerializer::FrameTile tile(KisTextureTileInfoPoolSP(0)); // TODO: fix the pool should never be null!
0178         tile.col = (*it)->tileCol();
0179         tile.row = (*it)->tileRow();
0180         tile.rect = (*it)->realPatchRect();
0181         tile.data = std::move((*it)->takePixelData());
0182 
0183         KIS_SAFE_ASSERT_RECOVER(tile.data.data()) { continue; }
0184 
0185         frame.frameTiles.push_back(std::move(tile));
0186     }
0187 
0188     FrameInfoSP frameInfo;
0189 
0190     if (m_d->lastSavedFullFrame.isValid()) {
0191         boost::optional<qreal> uniqueness = KisFrameDataSerializer::estimateFrameUniqueness(m_d->lastSavedFullFrame, frame, 0.01);
0192 
0193 
0194         if (uniqueness) {
0195 
0196             /**
0197              * The full-copying code is disabled intentionally:
0198              *
0199              * We should never remove user-visible data on basis of statistics. On smaller
0200              * images, like 32x32 pixels, there might be really subtle changes that
0201              * are important for the user. So we should use difference instead of dumb
0202              * copying.
0203              */
0204 #if 0
0205             if (*uniqueness == 0.0) {
0206                 FrameInfoSP baseFrameInfo = m_d->savedFrames[m_d->lastSavedFullFrameId];
0207                 frameInfo = toQShared(new FrameInfo(info->dirtyImageRect(),
0208                                                     imageBounds,
0209                                                     info->levelOfDetail(),
0210                                                     m_d->serializer,
0211                                                     baseFrameInfo));
0212 
0213             } else
0214 #endif
0215             if (*uniqueness < 0.5) {
0216                 FrameInfoSP baseFrameInfo = m_d->savedFrames[m_d->lastSavedFullFrameId];
0217 
0218                 KisFrameDataSerializer::subtractFrames(frame, m_d->lastSavedFullFrame);
0219                 frameInfo = toQShared(new FrameInfo(info->dirtyImageRect(),
0220                                                     imageBounds,
0221                                                     info->levelOfDetail(),
0222                                                     m_d->serializer,
0223                                                     baseFrameInfo,
0224                                                     frame));
0225             }
0226         }
0227     }
0228 
0229     if (!frameInfo) {
0230         frameInfo = toQShared(new FrameInfo(info->dirtyImageRect(),
0231                                             imageBounds,
0232                                             info->levelOfDetail(),
0233                                             m_d->serializer,
0234                                             frame));
0235     }
0236 
0237     m_d->savedFrames.insert(frameId, frameInfo);
0238 
0239     if (frameInfo->type() == FrameFull) {
0240         m_d->lastSavedFullFrame = std::move(frame);
0241         m_d->lastSavedFullFrameId = frameId;
0242     }
0243 }
0244 
0245 KisOpenGLUpdateInfoSP KisFrameCacheStore::loadFrame(int frameId, const KisOpenGLUpdateInfoBuilder &builder)
0246 {
0247     KisOpenGLUpdateInfoSP info = new KisOpenGLUpdateInfo();
0248     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(m_d->savedFrames.contains(frameId), info);
0249 
0250     FrameInfoSP frameInfo = m_d->savedFrames[frameId];
0251 
0252     info->assignDirtyImageRect(frameInfo->dirtyImageRect());
0253     info->assignLevelOfDetail(frameInfo->levelOfDetail());
0254 
0255     KisFrameDataSerializer::Frame frame;
0256 
0257     switch (frameInfo->type()) {
0258     case FrameFull:
0259         frame = m_d->serializer.loadFrame(frameInfo->frameDataId(), builder.textureInfoPool());
0260         m_d->lastLoadedBaseFrame = frame.clone();
0261         m_d->lastLoadedBaseFrameInfo = frameInfo;
0262         break;
0263     case FrameCopy: {
0264         FrameInfoSP baseFrameInfo = frameInfo->baseFrame();
0265         KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(baseFrameInfo, KisOpenGLUpdateInfoSP());
0266 
0267         if (baseFrameInfo == m_d->lastLoadedBaseFrameInfo) {
0268             frame = m_d->lastLoadedBaseFrame.clone();
0269         } else {
0270             frame = m_d->serializer.loadFrame(baseFrameInfo->frameDataId(), builder.textureInfoPool());
0271             m_d->lastLoadedBaseFrame = frame.clone();
0272             m_d->lastLoadedBaseFrameInfo = baseFrameInfo;
0273         }
0274         break;
0275     }
0276     case FrameDiff: {
0277         FrameInfoSP baseFrameInfo = frameInfo->baseFrame();
0278         KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(baseFrameInfo, KisOpenGLUpdateInfoSP());
0279 
0280         if (baseFrameInfo == m_d->lastLoadedBaseFrameInfo) {
0281             // noop
0282         } else {
0283             m_d->lastLoadedBaseFrame = m_d->serializer.loadFrame(baseFrameInfo->frameDataId(), builder.textureInfoPool());
0284             m_d->lastLoadedBaseFrameInfo = baseFrameInfo;
0285 
0286             KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(m_d->lastLoadedBaseFrame.isValid(), KisOpenGLUpdateInfoSP());
0287         }
0288 
0289         const KisFrameDataSerializer::Frame &baseFrame = m_d->lastLoadedBaseFrame;
0290         KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(baseFrame.isValid(), KisOpenGLUpdateInfoSP());
0291 
0292         frame = m_d->serializer.loadFrame(frameInfo->frameDataId(), builder.textureInfoPool());
0293         KisFrameDataSerializer::addFrames(frame, baseFrame);
0294         break;
0295     }
0296     }
0297 
0298     for (auto it = frame.frameTiles.begin(); it != frame.frameTiles.end(); ++it) {
0299         KisFrameDataSerializer::FrameTile &tile = *it;
0300 
0301         QRect patchRect = tile.rect;
0302 
0303         if (frameInfo->levelOfDetail()) {
0304             patchRect = KisLodTransform::upscaledRect(patchRect, frameInfo->levelOfDetail());
0305         }
0306 
0307         const QRect fullSizeTileRect =
0308             builder.calculatePhysicalTileRect(tile.col, tile.row,
0309                                               frameInfo->imageBounds(),
0310                                               frameInfo->levelOfDetail());
0311 
0312         KisTextureTileUpdateInfoSP tileInfo(
0313             new KisTextureTileUpdateInfo(tile.col, tile.row,
0314                                          fullSizeTileRect, patchRect,
0315                                          frameInfo->imageBounds(),
0316                                          frameInfo->levelOfDetail(),
0317                                          builder.textureInfoPool()));
0318 
0319         tileInfo->putPixelData(std::move(tile.data), builder.destinationColorSpace());
0320 
0321         info->tileList << tileInfo;
0322     }
0323 
0324     return info;
0325 }
0326 
0327 void KisFrameCacheStore::moveFrame(int srcFrameId, int dstFrameId)
0328 {
0329     KIS_SAFE_ASSERT_RECOVER_RETURN(srcFrameId != dstFrameId);
0330 
0331     KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->savedFrames.contains(srcFrameId));
0332 
0333     KIS_SAFE_ASSERT_RECOVER(!m_d->savedFrames.contains(dstFrameId)) {
0334         m_d->savedFrames.remove(dstFrameId);
0335     }
0336 
0337     m_d->savedFrames.insert(dstFrameId, m_d->savedFrames[srcFrameId]);
0338     m_d->savedFrames.remove(srcFrameId);
0339 
0340     if (m_d->lastSavedFullFrameId == srcFrameId) {
0341         m_d->lastSavedFullFrameId = dstFrameId;
0342     }
0343 }
0344 
0345 void KisFrameCacheStore::forgetFrame(int frameId)
0346 {
0347     KIS_SAFE_ASSERT_RECOVER_NOOP(m_d->savedFrames.contains(frameId));
0348 
0349     if (m_d->lastSavedFullFrameId == frameId) {
0350         m_d->lastSavedFullFrame = KisFrameDataSerializer::Frame();
0351         m_d->lastSavedFullFrameId = -1;
0352     }
0353 
0354     m_d->savedFrames.remove(frameId);
0355 }
0356 
0357 bool KisFrameCacheStore::hasFrame(int frameId) const
0358 {
0359     return m_d->savedFrames.contains(frameId);
0360 }
0361 
0362 int KisFrameCacheStore::frameLevelOfDetail(int frameId) const
0363 {
0364     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(m_d->savedFrames.contains(frameId), 0);
0365     return m_d->savedFrames[frameId]->levelOfDetail();
0366 }
0367 
0368 QRect KisFrameCacheStore::frameDirtyRect(int frameId) const
0369 {
0370     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(m_d->savedFrames.contains(frameId), QRect());
0371     return m_d->savedFrames[frameId]->dirtyImageRect();
0372 }