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 void ActiveFramePool::put(KeyFrame* key)
31 {
32  if (key == nullptr)
33  return;
34 
35  Q_ASSERT(key->pos() > 0);
36 
37  key->loadFile();
38 
39  auto it = mCacheFramesMap.find(key);
40  const bool keyExistsInPool = (it != mCacheFramesMap.end());
41  if (keyExistsInPool)
42  {
43  // move the keyframe to the front of the list, if the key already exists in frame pool
44  mCacheFramesList.erase(it->second);
45  }
46  mCacheFramesList.push_front(key);
47  mCacheFramesMap[key] = mCacheFramesList.begin();
48 
49  key->addEventListener(this);
50 
51  if (!keyExistsInPool)
52  {
53  mTotalUsedMemory += key->memoryUsage();
54  }
55 
56  discardLeastUsedFrames();
57 }
58 
59 void ActiveFramePool::clear()
60 {
61  for (KeyFrame* key : mCacheFramesList)
62  {
63  key->removeEventListner(this);
64  }
65  mCacheFramesList.clear();
66  mCacheFramesMap.clear();
67 }
68 
69 void ActiveFramePool::resize(quint64 memoryBudget)
70 {
71  memoryBudget = qMin(memoryBudget, quint64(1024) * 1024 * 1024 * 16); // 16GB
72  memoryBudget = qMax(memoryBudget, quint64(1024) * 1024 * 100); // 100MB
73  mMemoryBudgetInBytes = memoryBudget;
74  discardLeastUsedFrames();
75 }
76 
77 bool ActiveFramePool::isFrameInPool(KeyFrame* key)
78 {
79  auto it = mCacheFramesMap.find(key);
80  return (it != mCacheFramesMap.end());
81 }
82 
83 void ActiveFramePool::onKeyFrameDestroy(KeyFrame* key)
84 {
85  auto it = mCacheFramesMap.find(key);
86  if (it != mCacheFramesMap.end())
87  {
88  mCacheFramesList.erase(it->second);
89  mCacheFramesMap.erase(it);
90 
91  // Just recalculate the total usage
92  // Not safe to call key->memoryUsage() here cuz it's in the KeyFrame's destructor
93  recalcuateTotalUsedMemory();
94  }
95 }
96 
97 void ActiveFramePool::discardLeastUsedFrames()
98 {
99  while (mTotalUsedMemory > mMemoryBudgetInBytes)
100  {
101  list_iterator_t last = mCacheFramesList.end();
102  last--;
103 
104  KeyFrame* lastKeyFrame = *last;
105  unloadFrame(lastKeyFrame);
106 
107  mCacheFramesMap.erase(lastKeyFrame);
108  mCacheFramesList.pop_back();
109 
110  lastKeyFrame->removeEventListner(this);
111  }
112 }
113 
114 void ActiveFramePool::unloadFrame(KeyFrame* key)
115 {
116  mTotalUsedMemory -= key->memoryUsage();
117  key->unloadFile();
118 }
119 
120 void ActiveFramePool::recalcuateTotalUsedMemory()
121 {
122  mTotalUsedMemory = 0;
123  for (KeyFrame* key : mCacheFramesList)
124  {
125  mTotalUsedMemory += key->memoryUsage();
126  }
127 }