Ptex
PtexCache.cpp
Go to the documentation of this file.
1 /*
2 PTEX SOFTWARE
3 Copyright 2009 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 
107 #include "PtexPlatform.h"
108 #include <sys/types.h>
109 #include <sys/stat.h>
110 #include <stdlib.h>
111 #include <iostream>
112 #include <ctype.h>
113 #include "Ptexture.h"
114 #include "PtexReader.h"
115 #include "PtexCache.h"
116 
117 #ifdef GATHER_STATS
118 namespace PtexInternal {
119  CacheStats::~CacheStats() {
120  if (getenv("PTEX_STATS"))
121  print();
122  }
123 
124  void CacheStats::print()
125  {
126  if (nfilesOpened || ndataAllocated || nblocksRead) {
127  printf("Ptex Stats:\n");
128  printf(" nfilesOpened: %6d\n", nfilesOpened);
129  printf(" nfilesClosed: %6d\n", nfilesClosed);
130  printf(" ndataAllocated: %6d\n", ndataAllocated);
131  printf(" ndataFreed: %6d\n", ndataFreed);
132  printf(" nblocksRead: %6d\n", nblocksRead);
133  printf(" nseeks: %6d\n", nseeks);
134  if (nblocksRead)
135  printf(" avgReadSize: %6d\n", int(nbytesRead/nblocksRead));
136  if (nseeks)
137  printf(" avgSeqReadSize: %6d\n", int(nbytesRead/nseeks));
138  printf(" MbytesRead: %6.2f\n", nbytesRead/(1024.0*1024.0));
139  }
140  }
141 
142  CacheStats stats;
143 }
144 #endif
145 
147 {
148  // explicitly pop all unused items so that they are
149  // destroyed while cache is still valid
150  AutoLockCache locker(cachelock);
151  while (_unusedData.pop()) continue;
152  while (_unusedFiles.pop()) continue;
153 }
154 
156 {
157  assert(cachelock.locked());
158  _unusedFiles.extract(file);
160 }
161 
163 {
164  assert(cachelock.locked());
165  _unusedFiles.push(file);
167 }
168 
170 {
171  // cachelock should be locked, but might not be if cache is being deleted
173  STATS_INC(nfilesClosed);
174 }
175 
177 {
178  assert(cachelock.locked());
179  _unusedData.extract(data);
181  _unusedDataSize -= size;
182 }
183 
185 {
186  assert(cachelock.locked());
187  _unusedData.push(data);
189  _unusedDataSize += size;
190 }
191 
193  // cachelock should be locked, but might not be if cache is being deleted
195  _unusedDataSize -= size;
196  STATS_INC(ndataFreed);
197 }
198 
199 
202 {
203 public:
204  PtexReaderCache(int maxFiles, int maxMem, bool premultiply, PtexInputHandler* handler)
205  : PtexCacheImpl(maxFiles, maxMem),
206  _io(handler), _cleanupCount(0), _premultiply(premultiply)
207  {}
208 
210  {
211  // orphan all files since we're about to delete the file table
212  // and we don't want the base dtor to try to access it
213  purgeAll();
214  }
215 
216  virtual void setSearchPath(const char* path)
217  {
218  // get the open lock since the path is used during open operations
219  AutoMutex locker(openlock);
220 
221  // record path
222  _searchpath = path ? path : "";
223 
224  // split into dirs
225  _searchdirs.clear();
226 
227  if (path) {
228  const char* cp = path;
229  while (1) {
230  const char* delim = strchr(cp, ':');
231  if (!delim) {
232  if (*cp) _searchdirs.push_back(cp);
233  break;
234  }
235  int len = int(delim-cp);
236  if (len) _searchdirs.push_back(std::string(cp, len));
237  cp = delim+1;
238  }
239  }
240  }
241 
242  virtual const char* getSearchPath()
243  {
244  // get the open lock since the path is used during open operations
245  AutoMutex locker(openlock);
246  return _searchpath.c_str();
247  }
248 
249  virtual PtexTexture* get(const char* path, Ptex::String& error);
250 
251  virtual void purge(PtexTexture* texture)
252  {
253  // Note: we're assuming here that the texture was created from this cache
254  // and is therefore a PtexReader instance. dynamic_cast would be safer
255  // but we don't want to require rtti.
256  PtexReader* reader = static_cast<PtexReader*>(texture);
257  if (!reader) return;
258  purge(reader->path());
259  }
260 
261  virtual void purge(const char* filename)
262  {
263  AutoLockCache locker(cachelock);
264  FileMap::iterator iter = _files.find(filename);
265  if (iter != _files.end()) {
266  PtexReader* reader = iter->second;
267  if (reader && intptr_t(reader) != -1) {
268  reader->orphan();
269  iter->second = 0;
270  }
271  _files.erase(iter);
272  }
273  }
274 
275  virtual void purgeAll()
276  {
277  AutoLockCache locker(cachelock);
278  FileMap::iterator iter = _files.begin();
279  while (iter != _files.end()) {
280  PtexReader* reader = iter->second;
281  if (reader && intptr_t(reader) != -1) {
282  reader->orphan();
283  iter->second = 0;
284  }
285  iter = _files.erase(iter);
286  }
287  }
288 
289 
291  {
292  // remove blank file entries to keep map size in check
293  for (FileMap::iterator i = _files.begin(); i != _files.end();) {
294  if (i->second == 0) i = _files.erase(i);
295  else i++;
296  }
297  }
298 
299 
300 private:
302  std::string _searchpath;
303  std::vector<std::string> _searchdirs;
305  FileMap _files;
308 };
309 
310 
311 PtexTexture* PtexReaderCache::get(const char* filename, Ptex::String& error)
312 {
313  AutoLockCache locker(cachelock);
314 
315  // lookup reader in map
316  PtexReader* reader = _files[filename];
317  if (reader) {
318  // -1 means previous open attempt failed
319  if (intptr_t(reader) == -1) return 0;
320  reader->ref();
321  return reader;
322  }
323  else {
324  bool ok = true;
325 
326  // get open lock and make sure we still need to open
327  // temporarily release cache lock while we open acquire open lock
328  cachelock.unlock();
329  AutoMutex openlocker(openlock);
330  cachelock.lock();
331 
332  // lookup entry again (it might have changed in another thread)
333  PtexReader** entry = &_files[filename];
334 
335  if (*entry) {
336  // another thread opened it while we were waiting
337  if (intptr_t(*entry) == -1) return 0;
338  (*entry)->ref();
339  return *entry;
340  }
341 
342  // make a new reader
343  reader = new PtexReader((void**)entry, this, _premultiply, _io);
344 
345  // temporarily release cache lock while we open the file
346  cachelock.unlock();
347  std::string tmppath;
348  const char* pathToOpen = filename;
349  if (!_io) {
350  bool isAbsolute = (filename[0] == '/'
351 #ifdef WINDOWS
352  || filename[0] == '\\'
353  || (isalpha(filename[0]) && filename[1] == ':')
354 #endif
355  );
356  if (!isAbsolute && !_searchdirs.empty()) {
357  // file is relative, search in searchpath
358  tmppath.reserve(256); // minimize reallocs (will grow automatically)
359  bool found = false;
360  struct stat statbuf;
361  for (size_t i = 0, size = _searchdirs.size(); i < size; i++) {
362  tmppath = _searchdirs[i];
363  tmppath += "/";
364  tmppath += filename;
365  if (stat(tmppath.c_str(), &statbuf) == 0) {
366  found = true;
367  pathToOpen = tmppath.c_str();
368  break;
369  }
370  }
371  if (!found) {
372  std::string errstr = "Can't find ptex file: ";
373  errstr += filename;
374  error = errstr.c_str();
375  ok = false;
376  }
377  }
378  }
379  if (ok) ok = reader->open(pathToOpen, error);
380 
381  // reacquire cache lock
382  cachelock.lock();
383 
384  if (!ok) {
385  // open failed, clear parent ptr and unref to delete
386  *entry = reader; // to pass parent check in orphan()
387  reader->orphan();
388  reader->unref();
389  *entry = (PtexReader*)-1; // flag for future lookups
390  return 0;
391  }
392 
393  // successful open, record in _files map entry
394  *entry = reader;
395 
396  // clean up unused files
397  purgeFiles();
398 
399  // Cleanup map every so often so it doesn't get HUGE
400  // from being filled with blank entries from dead files.
401  // Note: this must be done while we still have the open lock!
402  if (++_cleanupCount >= 1000) {
403  _cleanupCount = 0;
405  }
406  }
407  return reader;
408 }
409 
410 PtexCache* PtexCache::create(int maxFiles, int maxMem, bool premultiply,
411  PtexInputHandler* handler)
412 {
413  // set default files to 100
414  if (maxFiles <= 0) maxFiles = 100;
415 
416  // set default memory to 100 MB
417  const int MB = 1024*1024;
418  if (maxMem <= 0) maxMem = 100 * MB;
419 
420  // if memory is < 1 MB, warn
421  if (maxMem < 1 * MB) {
422  std::cerr << "Warning, PtexCache created with < 1 MB" << std::endl;
423  }
424 
425  return new PtexReaderCache(maxFiles, maxMem, premultiply, handler);
426 }
427 
428 
virtual PtexTexture * get(const char *path, Ptex::String &error)
Open a texture.
Definition: PtexCache.cpp:311
long int _unusedDataSize
Definition: PtexCache.h:258
void setDataInUse(PtexLruItem *data, int size)
Definition: PtexCache.cpp:176
PtexReaderCache(int maxFiles, int maxMem, bool premultiply, PtexInputHandler *handler)
Definition: PtexCache.cpp:204
Memory-managed string.
Definition: Ptexture.h:303
bool erase(const char *key)
Will remove an entry. It will return TRUE if an entry was found.
Definition: PtexDict.h:555
iterator end()
Returns an iterator referencing the end of the table.
Definition: PtexDict.h:162
void unref()
Definition: PtexCache.h:272
File-handle and memory cache for reading ptex files.
Definition: Ptexture.h:634
virtual void setSearchPath(const char *path)
Set a search path for finding textures.
Definition: PtexCache.cpp:216
virtual const char * path()
Path that file was opened with.
Definition: PtexReader.h:80
int _unusedFileCount
Definition: PtexCache.h:257
virtual void purge(const char *filename)
Remove a texture file from the cache by pathname.
Definition: PtexCache.cpp:261
iterator begin()
Returns an iterator referencing the beginning of the table.
Definition: PtexDict.h:149
Platform-specific classes, functions, and includes.
static PtexCache * create(int maxFiles=0, int maxMem=0, bool premultiply=false, PtexInputHandler *handler=0)
Create a cache with the specified limits.
Definition: PtexCache.cpp:410
#define STATS_INC(x)
Definition: PtexCache.h:97
int _unusedDataCount
Definition: PtexCache.h:259
void removeFile()
Definition: PtexCache.cpp:169
bool pop()
Definition: PtexCache.h:178
std::string _searchpath
Definition: PtexCache.cpp:302
virtual const char * getSearchPath()
Query the search path.
Definition: PtexCache.cpp:242
void purgeFiles()
Definition: PtexCache.h:232
void setFileUnused(PtexLruItem *file)
Definition: PtexCache.cpp:162
void removeData(int size)
Definition: PtexCache.cpp:192
std::vector< std::string > _searchdirs
Definition: PtexCache.cpp:303
void push(PtexLruItem *node)
Definition: PtexCache.h:165
void setDataUnused(PtexLruItem *data, int size)
Definition: PtexCache.cpp:184
PtexLruList _unusedData
Definition: PtexCache.h:260
virtual void purgeAll()
Remove all texture files from the cache.
Definition: PtexCache.cpp:275
iterator find(const char *key)
Locates an entry, without creating a new one.
Definition: PtexDict.h:478
Mutex openlock
Definition: PtexCache.h:215
One item in a cache, typically an open file or a block of memory.
Definition: PtexCache.h:104
Cache for reading Ptex texture files.
Definition: PtexCache.cpp:201
For internal use only.
Definition: PtexCache.h:48
void setFileInUse(PtexLruItem *file)
Definition: PtexCache.cpp:155
void removeBlankEntries()
Definition: PtexCache.cpp:290
Interface for reading data from a ptex file.
Definition: Ptexture.h:439
PtexInputHandler * _io
Definition: PtexCache.cpp:301
CacheLock cachelock
Definition: PtexCache.h:216
virtual void purge(PtexTexture *texture)
Remove a texture file from the cache.
Definition: PtexCache.cpp:251
bool open(const char *path, Ptex::String &error)
Definition: PtexReader.cpp:63
Ptex cache implementation.
Definition: PtexCache.h:192
void orphan()
Definition: PtexCache.h:107
Automatically acquire and release lock within enclosing scope.
Definition: PtexMutex.h:58
friend class iterator
forward declared class
Definition: PtexDict.h:117
const char * c_str() const
Definition: Ptexture.h:312
PtexDict< PtexReader * > FileMap
Definition: PtexCache.cpp:304
Public API classes for reading, writing, caching, and filtering Ptex files.
PtexLruList _unusedFiles
Definition: PtexCache.h:260
Custom handler interface for intercepting and redirecting Ptex input stream calls.
Definition: Ptexture.h:585
void extract(PtexLruItem *node)
Definition: PtexCache.h:157