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