Ptex
PtexCache.cpp
Go to the documentation of this file.
1 /*
2 PTEX SOFTWARE
3 Copyright 2014 Disney Enterprises, Inc. All rights reserved
4 
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are
7 met:
8 
9  * Redistributions of source code must retain the above copyright
10  notice, this list of conditions and the following disclaimer.
11 
12  * Redistributions in binary form must reproduce the above copyright
13  notice, this list of conditions and the following disclaimer in
14  the documentation and/or other materials provided with the
15  distribution.
16 
17  * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
18  Studios" or the names of its contributors may NOT be used to
19  endorse or promote products derived from this software without
20  specific prior written permission from Walt Disney Pictures.
21 
22 Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
23 CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
24 BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
25 FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
26 IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
27 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
31 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
34 */
35 
69 #include "PtexPlatform.h"
70 #include <algorithm>
71 #include <sys/types.h>
72 #include <sys/stat.h>
73 #include <stdlib.h>
74 #include <iostream>
75 #include <ctype.h>
76 #include "Ptexture.h"
77 #include "PtexReader.h"
78 #include "PtexCache.h"
79 
80 
82 
84 {
85  // Unref the texture, and log the texture as recently used if the ref count becomes 0. Note: If
86  // the ref is negative after unref is called, it means the unref'd texture was not previously in
87  // use which is an application error. In this case, the texture was presumably already logged as
88  // recently used and we shouldn't log it again.
89  if (0 == unref()) {
90  _cache->logRecentlyUsed(this);
91  }
92 }
93 
94 
95 bool PtexReaderCache::findFile(const char*& filename, std::string& buffer, Ptex::String& error)
96 {
97  bool isAbsolute = (filename[0] == '/'
98 #ifdef PTEX_PLATFORM_WINDOWS
99  || filename[0] == '\\'
100  || (isalpha(filename[0]) && filename[1] == ':')
101 #endif
102  );
103  if (isAbsolute || _searchdirs.empty()) return true; // no need to search
104 
105  // file is relative, search in searchpath
106  buffer.reserve(256); // minimize reallocs (will grow automatically)
107  struct stat statbuf;
108  for (size_t i = 0, size = _searchdirs.size(); i < size; i++) {
109  buffer = _searchdirs[i];
110  buffer += "/";
111  buffer += filename;
112  if (stat(buffer.c_str(), &statbuf) == 0) {
113  filename = buffer.c_str();
114  return true;
115  }
116  }
117  // not found
118  std::string errstr = "Can't find ptex file: ";
119  errstr += filename;
120  error = errstr.c_str();
121  return false;
122 }
123 
124 
125 PtexTexture* PtexReaderCache::get(const char* filename, Ptex::String& error)
126 {
127  // lookup reader in map
128  StringKey key(filename);
129  PtexCachedReader* reader = _files.get(key);
130 
131  if (reader) {
132  if (!reader->ok()) return 0;
133  if (reader->pendingPurge()) {
134  // a previous purge attempt was made and file was busy. Try again now.
135  purge(reader);
136  }
137  reader->ref();
138  } else {
139  reader = new PtexCachedReader(_premultiply, _io, _err, this);
140  size_t newMemUsed = 0;
141  PtexCachedReader* newreader = reader;
142  reader = _files.tryInsert(key, reader, newMemUsed);
143  adjustMemUsed(newMemUsed);
144  if (reader != newreader) {
145  // another thread got here first
146  reader->ref();
147  delete newreader;
148  }
149  }
150 
151  if (reader->needToOpen()) {
152  std::string buffer;
153  const char* pathToOpen = filename;
154  // search for the file (unless we have an I/O handler)
155  if (_io || findFile(pathToOpen, buffer, error)) {
156  if (reader->open(pathToOpen, error)) {
157  reader->logOpen();
158  }
159  } else {
160  // flag reader as invalid so we don't try to open it again on next lookup
161  reader->invalidate();
162  }
163  }
164 
165  if (!reader->ok()) {
166  reader->unref();
167  return 0;
168  }
169 
170  return reader;
171 }
172 
173 PtexCache* PtexCache::create(int maxFiles, size_t maxMem, bool premultiply,
174  PtexInputHandler* inputHandler,
175  PtexErrorHandler* errorHandler)
176 {
177  // set default files to 100
178  if (maxFiles <= 0) maxFiles = 100;
179 
180  return new PtexReaderCache(maxFiles, maxMem, premultiply, inputHandler, errorHandler);
181 }
182 
183 
185 {
186  while (1) {
187  MruList* mruList = _mruList;
188  int slot = AtomicIncrement(&mruList->next)-1;
189  if (slot < maxMruFiles) {
190  mruList->files[slot] = reader;
191  processMru();
192  return;
193  }
194  // no mru slot available, process mru list and try again
195  do processMru();
196  while (_mruList->next >= maxMruFiles);
197  }
198 }
199 
201 {
202  // use a non-blocking lock so we can proceed as soon as space has been freed in the mru list
203  // (which happens almost immediately in the processMru thread that has the lock)
204  if (!_mruLock.trylock()) return;
205 
206  if (!_mruList->next) {
207  // nothing to do
208  _mruLock.unlock();
209  return;
210  }
211 
212  // switch mru buffers and reset slot counter so other threads can proceed immediately
213  MruList* mruList = _mruList;
215  _prevMruList = mruList;
216 
217  // extract relevant stats and add to open/active list
218  size_t memUsedChange = 0, filesOpenChange = 0;
219  int numFilesToProcess = std::min(int(mruList->next), maxMruFiles);
220  for (int i = 0; i < numFilesToProcess; ++i) {
221  PtexCachedReader* reader;
222  do { reader = mruList->files[i]; } while (!reader); // loop on (unlikely) race condition
223  mruList->files[i] = 0;
224  memUsedChange += reader->getMemUsedChange();
225  size_t opens = reader->getOpensChange();
226  size_t blockReads = reader->getBlockReadsChange();
227  filesOpenChange += opens;
228  if (opens || blockReads) {
229  _fileOpens += opens;
230  _blockReads += blockReads;
231  _openFiles.push(reader);
232  }
233  if (_maxMem) {
234  _activeFiles.push(reader);
235  }
236  }
237  AtomicStore(&mruList->next, 0);
238  adjustMemUsed(memUsedChange);
239  adjustFilesOpen(filesOpenChange);
240  _mruLock.unlock();
241 
243  if (_maxMem) {
245  }
246 }
247 
248 
250 {
251  while (_filesOpen > _maxFiles) {
252  // close one file not currently in use, and then check again
253  _mruLock.lock();
254  PtexCachedReader* reader = _openFiles.pop();
255  _mruLock.unlock();
256 
257  if (!reader) {
258  // no inactive open file available to close
259  break;
260  }
261  if (reader->tryClose()) {
262  _filesOpen = _filesOpen - 1;
263  }
264  }
265 }
266 
267 
269 {
270  size_t memUsedChangeTotal = 0;
271  while (_memUsed > _maxMem) {
272  // prune one texture not currently in use, and then check again
273  _mruLock.lock();
274  PtexCachedReader* reader = _activeFiles.pop();
275  _mruLock.unlock();
276  if (!reader) {
277  // no inactive texture available to prune
278  break;
279  }
280  size_t memUsedChange;
281  if (reader->tryPrune(memUsedChange)) {
282  // Note: after clearing, memUsedChange is negative
283  memUsedChangeTotal += memUsedChange;
284  }
285  }
286  if (memUsedChangeTotal) {
287  adjustMemUsed(memUsedChangeTotal);
288  }
289 }
290 
291 
293 {
294  PtexCachedReader* reader = static_cast<PtexCachedReader*>(texture);
295  reader->unref();
296  purge(reader);
297  reader->ref();
298 }
299 
300 
301 void PtexReaderCache::purge(const char* filename)
302 {
303  StringKey key(filename);
304  PtexCachedReader* reader = _files.get(key);
305  if (reader) purge(reader);
306 }
307 
309 {
310  size_t memUsedChange;
311  if (reader->tryPurge(memUsedChange)) {
312  adjustMemUsed(memUsedChange);
313  }
314 }
315 
317 {
318  size_t memUsedChange;
319  if (reader->tryPurge(memUsedChange)) {
320  memUsedChangeTotal += memUsedChange;
321  }
322 }
323 
325 {
326  Purger purger;
327  _files.foreach(purger);
329 }
330 
332 {
333  stats.memUsed = _memUsed;
334  stats.peakMemUsed = _peakMemUsed;
335  stats.filesOpen = _filesOpen;
337  stats.filesAccessed = _files.size();
338  stats.fileReopens = _fileOpens < stats.filesAccessed ? 0 : _fileOpens - stats.filesAccessed;
339  stats.blockReads = _blockReads;
340 }
341 
bool needToOpen() const
Definition: PtexReader.h:58
void adjustMemUsed(size_t amount)
Definition: PtexCache.h:266
PtexReaderCache * _cache
Definition: PtexCache.h:110
virtual PtexTexture * get(const char *path, Ptex::String &error)
Access a texture.
Definition: PtexCache.cpp:125
void pruneDataIfNeeded()
Definition: PtexCache.cpp:268
PtexCachedReader *volatile files[maxMruFiles]
Definition: PtexCache.h:307
uint64_t peakMemUsed
Definition: Ptexture.h:790
Memory-managed string.
Definition: Ptexture.h:298
void logOpen()
Definition: PtexReader.h:73
virtual void getStats(Stats &stats)
Get stats.
Definition: PtexCache.cpp:331
File-handle and memory cache for reading ptex files.
Definition: Ptexture.h:693
PtexErrorHandler * _err
Definition: PtexCache.h:294
PTEX_INLINE T AtomicIncrement(volatile T *target)
Definition: PtexPlatform.h:227
size_t _peakFilesOpen
Definition: PtexCache.h:317
size_t _maxMem
Definition: PtexCache.h:292
bool trylock()
Definition: PtexPlatform.h:144
uint64_t memUsed
Definition: Ptexture.h:789
Platform-specific classes, functions, and includes.
bool findFile(const char *&filename, std::string &buffer, Ptex::String &error)
Definition: PtexCache.cpp:95
void invalidate()
Definition: PtexReader.h:67
bool tryPurge(size_t &memUsedChange)
Definition: PtexCache.h:175
Custom handler interface redirecting Ptex error messages.
Definition: Ptexture.h:667
std::vector< std::string > _searchdirs
Definition: PtexCache.h:296
void operator()(PtexCachedReader *reader)
Definition: PtexCache.cpp:316
uint64_t filesOpen
Definition: Ptexture.h:791
void foreach(Fn &fn) const
Definition: PtexHashMap.h:251
uint32_t size() const
Definition: PtexHashMap.h:193
void logRecentlyUsed(PtexCachedReader *reader)
Definition: PtexCache.cpp:184
virtual void release()
Release resources held by this pointer (pointer becomes invalid).
Definition: PtexCache.cpp:83
Cache for reading Ptex texture files.
Definition: PtexCache.h:210
MruList *volatile _mruList
Definition: PtexCache.h:310
FileMap _files
Definition: PtexCache.h:298
T * pop()
Definition: PtexCache.h:95
size_t _maxFiles
Definition: PtexCache.h:291
uint64_t blockReads
Definition: Ptexture.h:795
PtexLruList< PtexCachedReader,&PtexCachedReader::_openFilesItem > _openFiles
Definition: PtexCache.h:313
uint64_t filesAccessed
Definition: Ptexture.h:793
static constexpr int maxMruFiles
Definition: PtexCache.h:304
size_t _blockReads
Definition: PtexCache.h:319
Value get(Key &key) const
Definition: PtexHashMap.h:197
PtexLruList< PtexCachedReader,&PtexCachedReader::_activeFilesItem > _activeFiles
Definition: PtexCache.h:314
size_t getOpensChange()
Definition: PtexCache.h:193
bool tryClose()
Definition: PtexReader.cpp:261
int32_t unref()
Definition: PtexCache.h:151
void push(T *node)
Definition: PtexCache.h:88
volatile size_t _filesOpen
Definition: PtexCache.h:301
Interface for reading data from a ptex file.
Definition: Ptexture.h:460
PtexInputHandler * _io
Definition: PtexCache.h:293
static PtexCache * create(int maxFiles, size_t maxMem, bool premultiply=false, PtexInputHandler *inputHandler=0, PtexErrorHandler *errorHandler=0)
Create a cache with the specified limits.
Definition: PtexCache.cpp:173
bool tryPrune(size_t &memUsedChange)
Definition: PtexCache.h:165
size_t getBlockReadsChange()
Definition: PtexCache.h:200
bool pendingPurge() const
Definition: PtexReader.h:63
uint64_t fileReopens
Definition: Ptexture.h:794
uint64_t peakFilesOpen
Definition: Ptexture.h:792
bool open(const char *path, Ptex::String &error)
Definition: PtexReader.cpp:141
volatile size_t _memUsed
Definition: PtexCache.h:300
void unlock()
Definition: PtexPlatform.h:145
void adjustFilesOpen(size_t amount)
Definition: PtexCache.h:272
bool ok() const
Definition: PtexReader.h:65
virtual void purgeAll()
Remove all texture files from the cache.
Definition: PtexCache.cpp:324
virtual void purge(PtexTexture *)
Remove a texture file from the cache.
Definition: PtexCache.cpp:292
void pruneFilesIfNeeded()
Definition: PtexCache.cpp:249
size_t getMemUsedChange()
Definition: PtexCache.h:186
MruList *volatile _prevMruList
Definition: PtexCache.h:311
PTEX_INLINE void AtomicStore(T volatile *target, T value)
Definition: PtexPlatform.h:286
size_t _fileOpens
Definition: PtexCache.h:318
const char * c_str() const
Definition: Ptexture.h:307
#define PTEX_NAMESPACE_END
Definition: PtexVersion.h:62
Public API classes for reading, writing, caching, and filtering Ptex files.
Value tryInsert(Key &key, Value value, size_t &newMemUsed)
Definition: PtexHashMap.h:220
void lock()
Definition: PtexPlatform.h:143
Custom handler interface for intercepting and redirecting Ptex input stream calls.
Definition: Ptexture.h:628
size_t _peakMemUsed
Definition: PtexCache.h:316