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 }