forked from NatronGitHub/Natron
-
Notifications
You must be signed in to change notification settings - Fork 0
/
CacheSerialization.h
236 lines (206 loc) · 9.16 KB
/
CacheSerialization.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
/* ***** BEGIN LICENSE BLOCK *****
* This file is part of Natron <https://natrongithub.github.io/>,
* (C) 2018-2021 The Natron developers
* (C) 2013-2018 INRIA and Alexandre Gauthier-Foichat
*
* Natron 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.
*
* Natron 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 Natron. If not, see <http://www.gnu.org/licenses/gpl-2.0.html>
* ***** END LICENSE BLOCK ***** */
#ifndef Engine_CacheSerialization_h
#define Engine_CacheSerialization_h
// ***** BEGIN PYTHON BLOCK *****
// from <https://docs.python.org/3/c-api/intro.html#include-files>:
// "Since Python may define some pre-processor definitions which affect the standard headers on some systems, you must include Python.h before any standard headers are included."
#include <Python.h>
// ***** END PYTHON BLOCK *****
#include "Global/Macros.h"
#include <list>
#include <set>
#include <cstddef>
#include <stdexcept>
#if !defined(Q_MOC_RUN) && !defined(SBK_RUN)
#include <boost/shared_ptr.hpp>
GCC_DIAG_UNUSED_LOCAL_TYPEDEFS_OFF
GCC_DIAG_OFF(unused-parameter)
// /opt/local/include/boost/serialization/smart_cast.hpp:254:25: warning: unused parameter 'u' [-Wunused-parameter]
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/serialization/utility.hpp>
#include <boost/serialization/list.hpp>
// /usr/local/include/boost/serialization/shared_ptr.hpp:112:5: warning: unused typedef 'boost_static_assert_typedef_112' [-Wunused-local-typedef]
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/split_member.hpp>
GCC_DIAG_UNUSED_LOCAL_TYPEDEFS_ON
GCC_DIAG_ON(unused-parameter)
#endif
#include "Engine/Cache.h"
#include "Engine/ImageSerialization.h"
#include "Engine/ImageParamsSerialization.h"
#include "Engine/FrameEntrySerialization.h"
#include "Engine/FrameParamsSerialization.h"
#include "Engine/EngineFwd.h"
// Note: these classes are used for cache serialization and do not have to maintain backward compatibility
#define SERIALIZED_ENTRY_INTRODUCES_SIZE 2
#define SERIALIZED_ENTRY_VERSION SERIALIZED_ENTRY_INTRODUCES_SIZE
//Beyond that percentage of occupation, the cache will start evicting LRU entries
#define NATRON_CACHE_LIMIT_PERCENT 0.9
///When defined, number of opened files, memory size and disk size of the cache are printed whenever there's activity.
//#define NATRON_DEBUG_CACHE
NATRON_NAMESPACE_ENTER
/*Saves cache to disk as a settings file.
*/
template<typename EntryType>
void
Cache<EntryType>::save(CacheTOC* tableOfContents)
{
clearInMemoryPortion(false);
{
QMutexLocker l(&_lock); // must be locked
for (CacheIterator it = _diskCache.begin(); it != _diskCache.end(); ++it) {
std::list<EntryTypePtr> & listOfValues = getValueFromIterator(it);
for (typename std::list<EntryTypePtr>::const_iterator it2 = listOfValues.begin(); it2 != listOfValues.end(); ++it2) {
if ( (*it2)->isStoredOnDisk() ) {
SerializedEntry serialization;
serialization.hash = (*it2)->getHashKey();
serialization.params = (*it2)->getParams();
serialization.key = (*it2)->getKey();
serialization.size = (*it2)->dataSize();
serialization.filePath = (*it2)->getFilePath();
serialization.dataOffsetInFile = (*it2)->getOffsetInFile();
(*it2)->syncBackingFile();
tableOfContents->push_back(serialization);
#ifdef DEBUG
if ( !_isTiled && !CacheAPI::checkFileNameMatchesHash(serialization.filePath, serialization.hash) ) {
qDebug() << "WARNING: Cache entry filename is not the same as the serialized hash key";
}
#endif
}
}
}
}
}
/*Restores the cache from disk.*/
template<typename EntryType>
void
Cache<EntryType>::restore(const CacheTOC & tableOfContents)
{
///Make sure the shared_ptrs live in this list and are destroyed not while under the lock
///so that the memory freeing (which might be expensive for large images) doesn't happen while under the lock
std::list<EntryTypePtr> entriesToBeDeleted;
std::set<QString> usedFilePaths;
for (typename CacheTOC::const_iterator it =
tableOfContents.begin(); it != tableOfContents.end(); ++it) {
if ( it->hash != it->key.getHash() ) {
/*
* If this warning is printed this means that the value computed by it->key()
* is different than the value stored prior to serialiazing this entry. In other words there're
* 2 possibilities:
* 1) The key has changed since it has been added to the cache: maybe you forgot to serialize some
* members of the key or you didn't save them correctly.
* 2) The hash key computation is unreliable and is depending upon changing or non-deterministic
* parameters which is wrong.
*/
qDebug() << "WARNING: serialized hash key different than the restored one";
}
#ifdef DEBUG
if ( !_isTiled && !checkFileNameMatchesHash(it->filePath, it->hash) ) {
qDebug() << "WARNING: Cache entry filename is not the same as the serialized hash key";
}
#endif
EntryType* value = NULL;
try {
value = new EntryType(it->key, it->params, this);
if (it->size != getTileSizeBytes()) {
delete value;
continue;
}
///This will not put the entry back into RAM, instead we just insert back the entry into the disk cache
value->restoreMetadataFromFile(it->size, it->filePath, it->dataOffsetInFile);
} catch (const std::exception & e) {
qDebug() << e.what();
delete value;
continue;
}
const std::string& filePath = value->getFilePath();
usedFilePaths.insert(QString::fromUtf8(filePath.c_str()));
{
QMutexLocker locker(&_lock);
sealEntry(EntryTypePtr(value), false /*inMemory*/);
}
}
// Remove from the cache all files that are not referenced by the table of contents
QString cachePath = getCachePath();
if (isTileCache()) {
QDir cacheFolder(cachePath);
QString absolutePath = cacheFolder.absolutePath();
QStringList etr = cacheFolder.entryList(QDir::NoDotAndDotDot);
for (QStringList::iterator it = etr.begin(); it!=etr.end(); ++it) {
QString entryFilePath = absolutePath + QLatin1Char('/') + *it;
std::set<QString>::iterator foundUsed = usedFilePaths.find(entryFilePath);
if (foundUsed == usedFilePaths.end()) {
cacheFolder.remove(*it);
}
}
} else {
for (U32 i = 0x00; i <= 0xF; ++i) {
for (U32 j = 0x00; j <= 0xF; ++j) {
char str[3] = { '0', '0', 0 };
str[0] += i;
str[1] += j;
QDir cacheFolder(cachePath + QLatin1Char('/') + QString::fromUtf8(str));
QString absolutePath = cacheFolder.absolutePath();
QStringList etr = cacheFolder.entryList(QDir::NoDotAndDotDot);
for (QStringList::iterator it = etr.begin(); it!=etr.end(); ++it) {
QString entryFilePath = absolutePath + QLatin1Char('/') + *it;
std::set<QString>::iterator foundUsed = usedFilePaths.find(entryFilePath);
if (foundUsed == usedFilePaths.end()) {
cacheFolder.remove(*it);
}
}
}
}
}
}
template<typename EntryType>
struct Cache<EntryType>::SerializedEntry
{
hash_type hash;
typename EntryType::key_type key;
ParamsTypePtr params;
std::size_t size; //< the data size in bytes
std::string filePath; //< we need to serialize it as several entries can have the same hash, hence we index them
std::size_t dataOffsetInFile;
SerializedEntry()
: hash(0)
, key()
, params()
, size(0)
, filePath()
, dataOffsetInFile(0)
{
}
template<class Archive>
void serialize(Archive & ar,
const unsigned int /*version*/)
{
ar & ::boost::serialization::make_nvp("Hash", hash);
ar & ::boost::serialization::make_nvp("Key", key);
ar & ::boost::serialization::make_nvp("Params", params);
ar & ::boost::serialization::make_nvp("Size", size);
ar & ::boost::serialization::make_nvp("Filename", filePath);
ar & ::boost::serialization::make_nvp("Offset", dataOffsetInFile);
}
};
NATRON_NAMESPACE_EXIT
#endif // Engine_CacheSerialization_h