1 /*
2 * Copyright (c) 2012-2016 Fredrik Mellbin
3 *
4 * This file is part of VapourSynth.
5 *
6 * VapourSynth is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * VapourSynth 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 GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with VapourSynth; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20 
21 #ifndef CACHEFILTER_H
22 #define CACHEFILTER_H
23 
24 #include "vscore.h"
25 #include <unordered_map>
26 #include <cassert>
27 
28 class VSCache {
29 private:
30     struct Node {
NodeNode31         inline Node() : key(-1) {}
NodeNode32         inline Node(int key, const PVideoFrame &frame) : key(key), frame(frame), weakFrame(frame), prevNode(0), nextNode(0) {}
33         int key;
34         PVideoFrame frame;
35         WVideoFrame weakFrame;
36         Node *prevNode;
37         Node *nextNode;
38     };
39 
40     Node *first;
41     Node *weakpoint;
42     Node *last;
43 
44     std::unordered_map<int, Node> hash;
45 
46     int maxSize;
47     int currentSize;
48     int maxHistorySize;
49     int historySize;
50 
51     bool fixedSize;
52 
53     int hits;
54     int nearMiss;
55     int farMiss;
56 
unlink(Node & n)57     inline void unlink(Node &n) {
58         if (&n == weakpoint)
59             weakpoint = weakpoint->nextNode;
60 
61         if (n.prevNode)
62             n.prevNode->nextNode = n.nextNode;
63 
64         if (n.nextNode)
65             n.nextNode->prevNode = n.prevNode;
66 
67         if (last == &n)
68             last = n.prevNode;
69 
70         if (first == &n)
71             first = n.nextNode;
72 
73         if (n.frame)
74             currentSize--;
75         else
76             historySize--;
77 
78         hash.erase(n.key);
79     }
80 
relink(const int key)81     inline PVideoFrame relink(const int key) {
82         auto i = hash.find(key);
83 
84         if (i == hash.end()) {
85             farMiss++;
86             return PVideoFrame();
87         }
88 
89         Node &n = i->second;
90 
91         if (!n.frame) {
92             nearMiss++;
93             try {
94                 n.frame = PVideoFrame(n.weakFrame);
95             } catch (std::bad_weak_ptr &) {
96                 return PVideoFrame();
97             }
98 
99             currentSize++;
100             historySize--;
101         }
102 
103         hits++;
104         Node *origWeakPoint = weakpoint;
105 
106         if (&n == origWeakPoint)
107             weakpoint = weakpoint->nextNode;
108 
109         if (first != &n) {
110             if (n.prevNode)
111                 n.prevNode->nextNode = n.nextNode;
112 
113             if (n.nextNode)
114                 n.nextNode->prevNode = n.prevNode;
115 
116             if (last == &n)
117                 last = n.prevNode;
118 
119             n.prevNode = 0;
120             n.nextNode = first;
121             first->prevNode = &n;
122             first = &n;
123         }
124 
125         if (!weakpoint) {
126             if (currentSize > maxSize) {
127                 weakpoint = last;
128                 weakpoint->frame.reset();
129             }
130         } else if (&n == origWeakPoint || historySize > maxHistorySize) {
131             weakpoint = weakpoint->prevNode;
132             weakpoint->frame.reset();
133         }
134 
135         assert(historySize <= maxHistorySize);
136 
137         return n.frame;
138     }
139 
140 public:
141     enum CacheAction {
142         caGrow,
143         caNoChange,
144         caShrink,
145         caClear
146     };
147 
148     VSCache(int maxSize, int maxHistorySize, bool fixedSize);
~VSCache()149     ~VSCache() {
150         clear();
151     }
152 
getMaxFrames()153     inline int getMaxFrames() const {
154         return maxSize;
155     }
setMaxFrames(int m)156     inline void setMaxFrames(int m) {
157         maxSize = m;
158         trim(maxSize, maxHistorySize);
159     }
getMaxHistory()160     inline int getMaxHistory() const {
161         return maxHistorySize;
162     }
setMaxHistory(int m)163     inline void setMaxHistory(int m) {
164         maxHistorySize = m;
165         trim(maxSize, maxHistorySize);
166     }
167 
size()168     inline size_t size() const {
169         return hash.size();
170     }
171 
clear()172     inline void clear() {
173         hash.clear();
174         first = nullptr;
175         last = nullptr;
176         weakpoint = nullptr;
177         currentSize = 0;
178         historySize = 0;
179         clearStats();
180     }
181 
clearStats()182     inline void clearStats() {
183         hits = 0;
184         nearMiss = 0;
185         farMiss = 0;
186     }
187 
188     bool insert(const int key, const PVideoFrame &object);
189     PVideoFrame object(const int key);
contains(const int key)190     inline bool contains(const int key) const {
191         return hash.count(key) > 0;
192     }
193     PVideoFrame operator[](const int key);
194 
195     bool remove(const int key);
196 
197 
198 
199     CacheAction recommendSize();
200 
201     void adjustSize(bool needMemory);
202 private:
203     void trim(int max, int maxHistory);
204 
205 };
206 
207 class CacheInstance {
208 public:
209     VSCache cache;
210     VSNodeRef *clip;
211     VSCore *core;
212     VSNode *node;
213     int lastN;
214     int numThreads;
215     bool makeLinear;
216 
CacheInstance(VSNodeRef * clip,VSCore * core,bool fixedSize)217     CacheInstance(VSNodeRef *clip, VSCore *core, bool fixedSize) : cache(20, 20, fixedSize), clip(clip), core(core), node(nullptr), lastN(-1), numThreads(0), makeLinear(false) {}
218 
addCache()219     void addCache() {
220         std::lock_guard<std::mutex> lock(core->cacheLock);
221         core->caches.insert(node);
222     }
223 
removeCache()224     void removeCache() {
225         std::lock_guard<std::mutex> lock(core->cacheLock);
226         core->caches.erase(node);
227     }
228 };
229 
230 void VS_CC cacheInitialize(VSConfigPlugin configFunc, VSRegisterFunction registerFunc, VSPlugin *plugin);
231 
232 #endif // CACHEFILTER_H
233