1 #ifndef PtexCache_h
2 #define PtexCache_h
3 
4 /*
5 PTEX SOFTWARE
6 Copyright 2014 Disney Enterprises, Inc.  All rights reserved
7 
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions are
10 met:
11 
12   * Redistributions of source code must retain the above copyright
13     notice, this list of conditions and the following disclaimer.
14 
15   * Redistributions in binary form must reproduce the above copyright
16     notice, this list of conditions and the following disclaimer in
17     the documentation and/or other materials provided with the
18     distribution.
19 
20   * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
21     Studios" or the names of its contributors may NOT be used to
22     endorse or promote products derived from this software without
23     specific prior written permission from Walt Disney Pictures.
24 
25 Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
26 CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
27 BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
28 FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
29 IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
30 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
31 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
32 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
33 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
34 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
37 */
38 
39 #include "PtexPlatform.h"
40 #include <cstddef>
41 
42 #include "PtexMutex.h"
43 #include "PtexHashMap.h"
44 #include "PtexReader.h"
45 
46 PTEX_NAMESPACE_BEGIN
47 
48 // Intrusive LRU list item (to be used only by PtexLruList)
49 class PtexLruItem
50 {
51     PtexLruItem* _prev;
52     PtexLruItem* _next;
53 
extract()54     void extract() {
55         _next->_prev = _prev;
56         _prev->_next = _next;
57         _next = _prev = this;
58     }
59 
60 public:
PtexLruItem()61     PtexLruItem() : _prev(this), _next(this) {}
62 
63     // add item to end of list (pointed to by _prev)
push(PtexLruItem * item)64     void push(PtexLruItem* item) {
65         item->extract();
66         _prev->_next = item;
67         item->_next = this;
68         item->_prev = _prev;
69         _prev = item;
70     }
71 
72     // remove item from front of list (pointed to by _next)
pop()73     PtexLruItem* pop() {
74         if (_next == this) return 0;
75         PtexLruItem* item = _next;
76         _next->extract();
77         return item;
78     }
79 };
80 
81 // Intrusive LRU list (with LRU item stored as member of T)
82 template<class T, PtexLruItem T::*item>
83 class PtexLruList
84 {
85     PtexLruItem _end;
86 
87 public:
push(T * node)88     void push(T* node)
89     {
90         // the item is added to the intrusive pointer specified by the template
91         // templatization allows more than one intrusive list to be in the object
92         _end.push(&(node->*item));
93     }
94 
pop()95     T* pop()
96     {
97         PtexLruItem* it = _end.pop();
98         // "it" points to the intrusive item, a member within T
99         // subtract off the pointer-to-member offset to get a pointer to the containing T object
100         static const T* dummy = 0;
101         static const std::ptrdiff_t itemOffset = (const char*)&(dummy->*item) - (const char*)dummy;
102         return it ? (T*) ((char*)it - itemOffset) : 0;
103     }
104 };
105 
106 class PtexReaderCache;
107 
108 class PtexCachedReader : public PtexReader
109 {
110     PtexReaderCache* _cache;
111     volatile int32_t _refCount;
112     size_t _memUsedAccountedFor;
113     size_t _opensAccountedFor;
114     size_t _blockReadsAccountedFor;
115     PtexLruItem _openFilesItem;
116     PtexLruItem _activeFilesItem;
117     friend class PtexReaderCache;
118 
trylock()119     bool trylock()
120     {
121         return AtomicCompareAndSwap(&_refCount, 0, -1);
122     }
123 
unlock()124     void unlock()
125     {
126         AtomicStore(&_refCount, 0);
127     }
128 
129 public:
PtexCachedReader(bool premultiply,PtexInputHandler * inputHandler,PtexErrorHandler * errorHandler,PtexReaderCache * cache)130     PtexCachedReader(bool premultiply, PtexInputHandler* inputHandler, PtexErrorHandler* errorHandler, PtexReaderCache* cache)
131         : PtexReader(premultiply, inputHandler, errorHandler), _cache(cache), _refCount(1),
132           _memUsedAccountedFor(0), _opensAccountedFor(0), _blockReadsAccountedFor(0)
133     {
134     }
135 
~PtexCachedReader()136     ~PtexCachedReader() {}
137 
ref()138     void ref() {
139         while (1) {
140             int32_t oldCount = _refCount;
141             if (oldCount >= 0 && AtomicCompareAndSwap(&_refCount, oldCount, oldCount+1))
142                 return;
143         }
144     }
145 
unref()146     int32_t unref() {
147         return AtomicDecrement(&_refCount);
148     }
149 
150     virtual void release();
151 
tryPrune(size_t & memUsedChange)152     bool tryPrune(size_t& memUsedChange) {
153         if (trylock()) {
154             prune();
155             memUsedChange = getMemUsedChange();
156             unlock();
157             return true;
158         }
159         return false;
160     }
161 
tryPurge(size_t & memUsedChange)162     bool tryPurge(size_t& memUsedChange) {
163         if (trylock()) {
164             purge();
165             memUsedChange = getMemUsedChange();
166             unlock();
167             return true;
168         }
169         setPendingPurge();
170         return false;
171     }
172 
getMemUsedChange()173     size_t getMemUsedChange() {
174         size_t memUsedTmp = _memUsed;
175         size_t result = memUsedTmp - _memUsedAccountedFor;
176         _memUsedAccountedFor = memUsedTmp;
177         return result;
178     }
179 
getOpensChange()180     size_t getOpensChange() {
181         size_t opensTmp = _opens;
182         size_t result = opensTmp - _opensAccountedFor;
183         _opensAccountedFor = opensTmp;
184         return result;
185     }
186 
getBlockReadsChange()187     size_t getBlockReadsChange() {
188         size_t blockReadsTmp = _blockReads;
189         size_t result = blockReadsTmp - _blockReadsAccountedFor;
190         _blockReadsAccountedFor = blockReadsTmp;
191         return result;
192     }
193 };
194 
195 
196 /** Cache for reading Ptex texture files */
197 class PtexReaderCache : public PtexCache
198 {
199 public:
PtexReaderCache(int maxFiles,size_t maxMem,bool premultiply,PtexInputHandler * inputHandler,PtexErrorHandler * errorHandler)200     PtexReaderCache(int maxFiles, size_t maxMem, bool premultiply, PtexInputHandler* inputHandler, PtexErrorHandler* errorHandler)
201         : _maxFiles(maxFiles), _maxMem(maxMem), _io(inputHandler), _err(errorHandler), _premultiply(premultiply),
202           _memUsed(sizeof(*this)), _filesOpen(0), _mruList(&_mruLists[0]), _prevMruList(&_mruLists[1]),
203           _peakMemUsed(0), _peakFilesOpen(0), _fileOpens(0), _blockReads(0)
204     {
205         memset((void*)&_mruLists[0], 0, sizeof(_mruLists));
206         CACHE_LINE_PAD_INIT(_memUsed); // keep cppcheck happy
207         CACHE_LINE_PAD_INIT(_filesOpen);
208         CACHE_LINE_PAD_INIT(_mruLock);
209     }
210 
~PtexReaderCache()211     ~PtexReaderCache()
212     {}
213 
release()214     virtual void release() { delete this; }
215 
setSearchPath(const char * path)216     virtual void setSearchPath(const char* path)
217     {
218         // record path
219         _searchpath = path ? path : "";
220 
221         // split into dirs
222         _searchdirs.clear();
223 
224         if (path) {
225             const char* cp = path;
226             while (1) {
227                 const char* delim = strchr(cp, ':');
228                 if (!delim) {
229                     if (*cp) _searchdirs.push_back(cp);
230                     break;
231                 }
232                 int len = int(delim-cp);
233                 if (len) _searchdirs.push_back(std::string(cp, len));
234                 cp = delim+1;
235             }
236         }
237     }
238 
getSearchPath()239     virtual const char* getSearchPath()
240     {
241         return _searchpath.c_str();
242     }
243 
244     virtual PtexTexture* get(const char* path, Ptex::String& error);
245 
246     virtual void purge(PtexTexture* /*texture*/);
247     virtual void purge(const char* /*filename*/);
248     virtual void purgeAll();
249     virtual void getStats(Stats& stats);
250 
251     void purge(PtexCachedReader* reader);
252 
adjustMemUsed(size_t amount)253     void adjustMemUsed(size_t amount) {
254         if (amount) {
255             size_t memUsed = AtomicAdd(&_memUsed, amount);
256             _peakMemUsed = std::max(_peakMemUsed, memUsed);
257         }
258     }
adjustFilesOpen(size_t amount)259     void adjustFilesOpen(size_t amount) {
260         if (amount) {
261             size_t filesOpen = AtomicAdd(&_filesOpen, amount);
262             _peakFilesOpen = std::max(_peakFilesOpen, filesOpen);
263         }
264     }
265     void logRecentlyUsed(PtexCachedReader* reader);
266 
267 private:
268     struct Purger {
269         size_t memUsedChangeTotal;
PurgerPurger270         Purger() : memUsedChangeTotal(0) {}
271         void operator() (PtexCachedReader* reader);
272     };
273 
274     bool findFile(const char*& filename, std::string& buffer, Ptex::String& error);
275     void processMru();
276     void pruneFiles();
277     void pruneData();
278     size_t _maxFiles;
279     size_t _maxMem;
280     PtexInputHandler* _io;
281     PtexErrorHandler* _err;
282     std::string _searchpath;
283     std::vector<std::string> _searchdirs;
284     typedef PtexHashMap<StringKey,PtexCachedReader*> FileMap;
285     FileMap _files;
286     bool _premultiply;
287     volatile size_t _memUsed; CACHE_LINE_PAD(_memUsed,size_t);
288     volatile size_t _filesOpen; CACHE_LINE_PAD(_filesOpen,size_t);
289     Mutex _mruLock; CACHE_LINE_PAD(_mruLock,Mutex);
290 
291     static const int numMruFiles = 50;
292     struct MruList {
293         volatile int next;
294         PtexCachedReader* volatile files[numMruFiles];
295     };
296     MruList _mruLists[2];
297     MruList* volatile _mruList;
298     MruList* volatile _prevMruList;
299 
300     PtexLruList<PtexCachedReader, &PtexCachedReader::_openFilesItem> _openFiles;
301     PtexLruList<PtexCachedReader, &PtexCachedReader::_activeFilesItem> _activeFiles;
302 
303     size_t _peakMemUsed;
304     size_t _peakFilesOpen;
305     size_t _fileOpens;
306     size_t _blockReads;
307 };
308 
309 PTEX_NAMESPACE_END
310 
311 #endif
312