File indexing completed on 2024-05-12 16:01:48

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 coicides 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         frame.frameTiles.push_back(std::move(tile));
0184     }
0185 
0186     FrameInfoSP frameInfo;
0187 
0188     if (m_d->lastSavedFullFrame.isValid()) {
0189         boost::optional<qreal> uniqueness = KisFrameDataSerializer::estimateFrameUniqueness(m_d->lastSavedFullFrame, frame, 0.01);
0190 
0191 
0192         if (uniqueness) {
0193 
0194             /**
0195              * The full-copying code is disabled intentionally:
0196              *
0197              * We should never remove user-visible data on basis of statistics. On smaller
0198              * images, like 32x32 pixels, there might be really subtle changes that
0199              * are important for the user. So we should use difference instead of dumb
0200              * copying.
0201              */
0202 #if 0
0203             if (*uniqueness == 0.0) {
0204                 FrameInfoSP baseFrameInfo = m_d->savedFrames[m_d->lastSavedFullFrameId];
0205                 frameInfo = toQShared(new FrameInfo(info->dirtyImageRect(),
0206                                                     imageBounds,
0207                                                     info->levelOfDetail(),
0208                                                     m_d->serializer,
0209                                                     baseFrameInfo));
0210 
0211             } else
0212 #endif
0213             if (*uniqueness < 0.5) {
0214                 FrameInfoSP baseFrameInfo = m_d->savedFrames[m_d->lastSavedFullFrameId];
0215 
0216                 KisFrameDataSerializer::subtractFrames(frame, m_d->lastSavedFullFrame);
0217                 frameInfo = toQShared(new FrameInfo(info->dirtyImageRect(),
0218                                                     imageBounds,
0219                                                     info->levelOfDetail(),
0220                                                     m_d->serializer,
0221                                                     baseFrameInfo,
0222                                                     frame));
0223             }
0224         }
0225     }
0226 
0227     if (!frameInfo) {
0228         frameInfo = toQShared(new FrameInfo(info->dirtyImageRect(),
0229                                             imageBounds,
0230                                             info->levelOfDetail(),
0231                                             m_d->serializer,
0232                                             frame));
0233     }
0234 
0235     m_d->savedFrames.insert(frameId, frameInfo);
0236 
0237     if (frameInfo->type() == FrameFull) {
0238         m_d->lastSavedFullFrame = std::move(frame);
0239         m_d->lastSavedFullFrameId = frameId;
0240     }
0241 }
0242 
0243 KisOpenGLUpdateInfoSP KisFrameCacheStore::loadFrame(int frameId, const KisOpenGLUpdateInfoBuilder &builder)
0244 {
0245     KisOpenGLUpdateInfoSP info = new KisOpenGLUpdateInfo();
0246     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(m_d->savedFrames.contains(frameId), info);
0247 
0248     FrameInfoSP frameInfo = m_d->savedFrames[frameId];
0249 
0250     info->assignDirtyImageRect(frameInfo->dirtyImageRect());
0251     info->assignLevelOfDetail(frameInfo->levelOfDetail());
0252 
0253     KisFrameDataSerializer::Frame frame;
0254 
0255     switch (frameInfo->type()) {
0256     case FrameFull:
0257         frame = m_d->serializer.loadFrame(frameInfo->frameDataId(), builder.textureInfoPool());
0258         m_d->lastLoadedBaseFrame = frame.clone();
0259         m_d->lastLoadedBaseFrameInfo = frameInfo;
0260         break;
0261     case FrameCopy: {
0262         FrameInfoSP baseFrameInfo = frameInfo->baseFrame();
0263         KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(baseFrameInfo, KisOpenGLUpdateInfoSP());
0264 
0265         if (baseFrameInfo == m_d->lastLoadedBaseFrameInfo) {
0266             frame = m_d->lastLoadedBaseFrame.clone();
0267         } else {
0268             frame = m_d->serializer.loadFrame(baseFrameInfo->frameDataId(), builder.textureInfoPool());
0269             m_d->lastLoadedBaseFrame = frame.clone();
0270             m_d->lastLoadedBaseFrameInfo = baseFrameInfo;
0271         }
0272         break;
0273     }
0274     case FrameDiff: {
0275         FrameInfoSP baseFrameInfo = frameInfo->baseFrame();
0276         KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(baseFrameInfo, KisOpenGLUpdateInfoSP());
0277 
0278         if (baseFrameInfo == m_d->lastLoadedBaseFrameInfo) {
0279             // noop
0280         } else {
0281             m_d->lastLoadedBaseFrame = m_d->serializer.loadFrame(baseFrameInfo->frameDataId(), builder.textureInfoPool());
0282             m_d->lastLoadedBaseFrameInfo = baseFrameInfo;
0283 
0284             KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(m_d->lastLoadedBaseFrame.isValid(), KisOpenGLUpdateInfoSP());
0285         }
0286 
0287         const KisFrameDataSerializer::Frame &baseFrame = m_d->lastLoadedBaseFrame;
0288         KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(baseFrame.isValid(), KisOpenGLUpdateInfoSP());
0289 
0290         frame = m_d->serializer.loadFrame(frameInfo->frameDataId(), builder.textureInfoPool());
0291         KisFrameDataSerializer::addFrames(frame, baseFrame);
0292         break;
0293     }
0294     }
0295 
0296     for (auto it = frame.frameTiles.begin(); it != frame.frameTiles.end(); ++it) {
0297         KisFrameDataSerializer::FrameTile &tile = *it;
0298 
0299         QRect patchRect = tile.rect;
0300 
0301         if (frameInfo->levelOfDetail()) {
0302             patchRect = KisLodTransform::upscaledRect(patchRect, frameInfo->levelOfDetail());
0303         }
0304 
0305         const QRect fullSizeTileRect =
0306             builder.calculatePhysicalTileRect(tile.col, tile.row,
0307                                               frameInfo->imageBounds(),
0308                                               frameInfo->levelOfDetail());
0309 
0310         KisTextureTileUpdateInfoSP tileInfo(
0311             new KisTextureTileUpdateInfo(tile.col, tile.row,
0312                                          fullSizeTileRect, patchRect,
0313                                          frameInfo->imageBounds(),
0314                                          frameInfo->levelOfDetail(),
0315                                          builder.textureInfoPool()));
0316 
0317         tileInfo->putPixelData(std::move(tile.data), builder.destinationColorSpace());
0318 
0319         info->tileList << tileInfo;
0320     }
0321 
0322     return info;
0323 }
0324 
0325 void KisFrameCacheStore::moveFrame(int srcFrameId, int dstFrameId)
0326 {
0327     KIS_SAFE_ASSERT_RECOVER_RETURN(srcFrameId != dstFrameId);
0328 
0329     KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->savedFrames.contains(srcFrameId));
0330 
0331     KIS_SAFE_ASSERT_RECOVER(!m_d->savedFrames.contains(dstFrameId)) {
0332         m_d->savedFrames.remove(dstFrameId);
0333     }
0334 
0335     m_d->savedFrames.insert(dstFrameId, m_d->savedFrames[srcFrameId]);
0336     m_d->savedFrames.remove(srcFrameId);
0337 
0338     if (m_d->lastSavedFullFrameId == srcFrameId) {
0339         m_d->lastSavedFullFrameId = dstFrameId;
0340     }
0341 }
0342 
0343 void KisFrameCacheStore::forgetFrame(int frameId)
0344 {
0345     KIS_SAFE_ASSERT_RECOVER_NOOP(m_d->savedFrames.contains(frameId));
0346 
0347     if (m_d->lastSavedFullFrameId == frameId) {
0348         m_d->lastSavedFullFrame = KisFrameDataSerializer::Frame();
0349         m_d->lastSavedFullFrameId = -1;
0350     }
0351 
0352     m_d->savedFrames.remove(frameId);
0353 }
0354 
0355 bool KisFrameCacheStore::hasFrame(int frameId) const
0356 {
0357     return m_d->savedFrames.contains(frameId);
0358 }
0359 
0360 int KisFrameCacheStore::frameLevelOfDetail(int frameId) const
0361 {
0362     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(m_d->savedFrames.contains(frameId), 0);
0363     return m_d->savedFrames[frameId]->levelOfDetail();
0364 }
0365 
0366 QRect KisFrameCacheStore::frameDirtyRect(int frameId) const
0367 {
0368     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(m_d->savedFrames.contains(frameId), QRect());
0369     return m_d->savedFrames[frameId]->dirtyImageRect();
0370 }