All Classes Namespaces Functions Variables Enumerations Properties Pages
activeframepool.cpp
1 /*
2 
3 Pencil2D - Traditional Animation Software
4 Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon
5 Copyright (C) 2012-2020 Matthew Chiawen Chang
6 
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; version 2 of the License.
10 
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15 
16 */
17 
18 #include "activeframepool.h"
19 #include "keyframe.h"
20 #include "pencildef.h"
21 
22 
23 ActiveFramePool::ActiveFramePool()
24 {
25  Q_ASSERT(mMemoryBudgetInBytes >= (1024 * 1024 * 100)); // at least 100MB
26 }
27 
28 ActiveFramePool::~ActiveFramePool()
29 {
30  clear();
31 }
32 
33 void ActiveFramePool::put(KeyFrame* key)
34 {
35  if (key == nullptr)
36  return;
37 
38  Q_ASSERT(key->pos() > 0);
39 
40  key->loadFile();
41 
42  auto it = mCacheFramesMap.find(key);
43  const bool keyExistsInPool = (it != mCacheFramesMap.end());
44  if (keyExistsInPool)
45  {
46  // move the keyframe to the front of the list, if the key already exists in frame pool
47  mCacheFramesList.erase(it->second);
48  }
49  mCacheFramesList.push_front(key);
50  mCacheFramesMap[key] = mCacheFramesList.begin();
51 
52  key->addEventListener(this);
53 
54  if (!keyExistsInPool)
55  {
56  mTotalUsedMemory += key->memoryUsage();
57  }
58 
59  discardLeastUsedFrames();
60 }
61 
62 void ActiveFramePool::clear()
63 {
64  for (KeyFrame* key : mCacheFramesList)
65  {
66  key->removeEventListner(this);
67  }
68  mCacheFramesList.clear();
69  mCacheFramesMap.clear();
70 }
71 
72 void ActiveFramePool::resize(quint64 memoryBudget)
73 {
74  memoryBudget = qMin(memoryBudget, quint64(1024) * 1024 * 1024 * 16); // 16GB
75  memoryBudget = qMax(memoryBudget, quint64(1024) * 1024 * 100); // 100MB
76  mMemoryBudgetInBytes = memoryBudget;
77  discardLeastUsedFrames();
78 }
79 
80 bool ActiveFramePool::isFrameInPool(KeyFrame* key)
81 {
82  auto it = mCacheFramesMap.find(key);
83  return (it != mCacheFramesMap.end());
84 }
85 
86 void ActiveFramePool::setMinFrameCount(size_t frameCount)
87 {
88  mMinFrameCount = frameCount;
89 }
90 
91 void ActiveFramePool::onKeyFrameDestroy(KeyFrame* key)
92 {
93  auto it = mCacheFramesMap.find(key);
94  if (it != mCacheFramesMap.end())
95  {
96  mCacheFramesList.erase(it->second);
97  mCacheFramesMap.erase(it);
98 
99  // Just recalculate the total usage
100  // Not safe to call key->memoryUsage() here cuz it's in the KeyFrame's destructor
101  recalcuateTotalUsedMemory();
102  }
103 }
104 
105 void ActiveFramePool::discardLeastUsedFrames()
106 {
107  while ((mTotalUsedMemory > mMemoryBudgetInBytes) && (mCacheFramesList.size() > mMinFrameCount))
108  {
109  list_iterator_t last = mCacheFramesList.end();
110  last--;
111 
112  KeyFrame* lastKeyFrame = *last;
113  unloadFrame(lastKeyFrame);
114 
115  mCacheFramesMap.erase(lastKeyFrame);
116  mCacheFramesList.pop_back();
117 
118  lastKeyFrame->removeEventListner(this);
119  }
120 }
121 
122 void ActiveFramePool::unloadFrame(KeyFrame* key)
123 {
124  mTotalUsedMemory -= key->memoryUsage();
125  key->unloadFile();
126 }
127 
128 void ActiveFramePool::recalcuateTotalUsedMemory()
129 {
130  mTotalUsedMemory = 0;
131  for (KeyFrame* key : mCacheFramesList)
132  {
133  mTotalUsedMemory += key->memoryUsage();
134  }
135 }