Ptex: PtexCache.cpp Source File

Ptex

- Main Page
- Related Pages
- Namespaces
- Classes
- Files

- File List
- File Members

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
146 PtexCacheImpl::~PtexCacheImpl()
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
155 void PtexCacheImpl::setFileInUse(PtexLruItem* file)
156 {
157 assert(cachelock.locked());
158 _unusedFiles.extract(file);
159 _unusedFileCount--;
160 }
161
162 void PtexCacheImpl::setFileUnused(PtexLruItem* file)
163 {
164 assert(cachelock.locked());
165 _unusedFiles.push(file);
166 _unusedFileCount++;
167 }
168
169 void PtexCacheImpl::removeFile()
170 {
171 // cachelock should be locked, but might not be if cache is being deleted
172 _unusedFileCount--;
173 STATS_INC(nfilesClosed);
174 }
175
176 void PtexCacheImpl::setDataInUse(PtexLruItem* data, int size)
177 {
178 assert(cachelock.locked());
179 _unusedData.extract(data);
180 _unusedDataCount--;
181 _unusedDataSize -= size;
182 }
183
184 void PtexCacheImpl::setDataUnused(PtexLruItem* data, int size)
185 {
186 assert(cachelock.locked());
187 _unusedData.push(data);
188 _unusedDataCount++;
189 _unusedDataSize += size;
190 }
191
192 void PtexCacheImpl::removeData(int size) {
193 // cachelock should be locked, but might not be if cache is being deleted
194 _unusedDataCount--;
195 _unusedDataSize -= size;
196 STATS_INC(ndataFreed);
197 }
198
199
201 class PtexReaderCache : public PtexCacheImpl
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
209 ~PtexReaderCache()
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
290 void removeBlankEntries()
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:
301 PtexInputHandler* _io;
302 std::string _searchpath;
303 std::vector<std::string> _searchdirs;
304 typedef PtexDict<PtexReader*> FileMap;
305 FileMap _files;
306 int _cleanupCount;
307 bool _premultiply;
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;
404 removeBlankEntries();
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
PtexReaderCache::get
virtual PtexTexture * get(const char *path, Ptex::String &error)
Open a texture.
Definition: PtexCache.cpp:311
PtexCacheImpl::_unusedDataSize
long int _unusedDataSize
Definition: PtexCache.h:258
PtexCacheImpl::setDataInUse
void setDataInUse(PtexLruItem *data, int size)
Definition: PtexCache.cpp:176
PtexReaderCache::PtexReaderCache
PtexReaderCache(int maxFiles, int maxMem, bool premultiply, PtexInputHandler *handler)
Definition: PtexCache.cpp:204
Ptex::String
Memory-managed string.
Definition: Ptexture.h:303
PtexDict::erase
bool erase(const char *key)
Will remove an entry. It will return TRUE if an entry was found.
Definition: PtexDict.h:555
PtexDict::end
iterator end()
Returns an iterator referencing the end of the table.
Definition: PtexDict.h:162
PtexCachedFile::unref
void unref()
Definition: PtexCache.h:272
PtexCache
File-handle and memory cache for reading ptex files.
Definition: Ptexture.h:634
PtexReaderCache::setSearchPath
virtual void setSearchPath(const char *path)
Set a search path for finding textures.
Definition: PtexCache.cpp:216
PtexReader::path
virtual const char * path()
Path that file was opened with.
Definition: PtexReader.h:80
PtexCacheImpl::_unusedFileCount
int _unusedFileCount
Definition: PtexCache.h:257
PtexReaderCache::purge
virtual void purge(const char *filename)
Remove a texture file from the cache by pathname.
Definition: PtexCache.cpp:261
PtexDict::begin
iterator begin()
Returns an iterator referencing the beginning of the table.
Definition: PtexDict.h:149
PtexPlatform.h
Platform-specific classes, functions, and includes.
PtexCache.h
PtexCache::create
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
PtexReaderCache::_cleanupCount
int _cleanupCount
Definition: PtexCache.cpp:306
STATS_INC
#define STATS_INC(x)
Definition: PtexCache.h:97
PtexCacheImpl::_unusedDataCount
int _unusedDataCount
Definition: PtexCache.h:259
PtexCacheImpl::removeFile
void removeFile()
Definition: PtexCache.cpp:169
PtexLruList::pop
bool pop()
Definition: PtexCache.h:178
PtexReaderCache::_searchpath
std::string _searchpath
Definition: PtexCache.cpp:302
PtexReaderCache::getSearchPath
virtual const char * getSearchPath()
Query the search path.
Definition: PtexCache.cpp:242
PtexCacheImpl::purgeFiles
void purgeFiles()
Definition: PtexCache.h:232
PtexCacheImpl::setFileUnused
void setFileUnused(PtexLruItem *file)
Definition: PtexCache.cpp:162
PtexCacheImpl::removeData
void removeData(int size)
Definition: PtexCache.cpp:192
PtexReaderCache::_searchdirs
std::vector< std::string > _searchdirs
Definition: PtexCache.cpp:303
PtexLruList::push
void push(PtexLruItem *node)
Definition: PtexCache.h:165
PtexCacheImpl::setDataUnused
void setDataUnused(PtexLruItem *data, int size)
Definition: PtexCache.cpp:184
PtexCacheImpl::~PtexCacheImpl
~PtexCacheImpl()
Definition: PtexCache.cpp:146
PtexInternal::_SpinLock::unlock
void unlock()
Definition: PtexPlatform.h:146
PtexCacheImpl::_unusedData
PtexLruList _unusedData
Definition: PtexCache.h:260
PtexReaderCache::purgeAll
virtual void purgeAll()
Remove all texture files from the cache.
Definition: PtexCache.cpp:275
PtexReader
Definition: PtexReader.h:72
PtexDict::find
iterator find(const char *key)
Locates an entry, without creating a new one.
Definition: PtexDict.h:478
PtexCacheImpl::openlock
Mutex openlock
Definition: PtexCache.h:215
PtexLruItem
One item in a cache, typically an open file or a block of memory.
Definition: PtexCache.h:104
PtexReaderCache
Cache for reading Ptex texture files.
Definition: PtexCache.cpp:201
PtexReaderCache::_files
FileMap _files
Definition: PtexCache.cpp:305
PtexInternal::_SpinLock::lock
void lock()
Definition: PtexPlatform.h:145
PtexInternal
For internal use only.
Definition: PtexCache.h:48
PtexReaderCache::_premultiply
bool _premultiply
Definition: PtexCache.cpp:307
PtexDict< PtexReader * >
PtexCacheImpl::setFileInUse
void setFileInUse(PtexLruItem *file)
Definition: PtexCache.cpp:155
PtexReaderCache::removeBlankEntries
void removeBlankEntries()
Definition: PtexCache.cpp:290
PtexTexture
Interface for reading data from a ptex file.
Definition: Ptexture.h:439
PtexReaderCache::_io
PtexInputHandler * _io
Definition: PtexCache.cpp:301
PtexCacheImpl::cachelock
CacheLock cachelock
Definition: PtexCache.h:216
PtexReaderCache::purge
virtual void purge(PtexTexture *texture)
Remove a texture file from the cache.
Definition: PtexCache.cpp:251
PtexReader::open
bool open(const char *path, Ptex::String &error)
Definition: PtexReader.cpp:63
PtexCacheImpl
Ptex cache implementation.
Definition: PtexCache.h:192
PtexLruItem::orphan
void orphan()
Definition: PtexCache.h:107
PtexInternal::AutoLock
Automatically acquire and release lock within enclosing scope.
Definition: PtexMutex.h:58
PtexDict< PtexReader * >::iterator
friend class iterator
forward declared class
Definition: PtexDict.h:117
PtexReader.h
Ptex::String::c_str
const char * c_str() const
Definition: Ptexture.h:312
PtexReaderCache::FileMap
PtexDict< PtexReader * > FileMap
Definition: PtexCache.cpp:304
PtexReaderCache::~PtexReaderCache
~PtexReaderCache()
Definition: PtexCache.cpp:209
Ptexture.h
Public API classes for reading, writing, caching, and filtering Ptex files.
PtexCacheImpl::_unusedFiles
PtexLruList _unusedFiles
Definition: PtexCache.h:260
PtexInputHandler
Custom handler interface for intercepting and redirecting Ptex input stream calls.
Definition: Ptexture.h:585
PtexCachedFile::ref
void ref()
Definition: PtexCache.h:271
PtexLruList::extract
void extract(PtexLruItem *node)
Definition: PtexCache.h:157