#include "PtexPlatform.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <iostream>
#include <limits.h>
#include "Ptexture.h"
#include "PtexReader.h"
#include "PtexCache.h"
#ifdef GATHER_STATS
namespace PtexInternal {
CacheStats::~CacheStats() {
if (getenv("PTEX_STATS"))
print();
}
void CacheStats::print()
{
if (nfilesOpened || ndataAllocated || nblocksRead) {
printf("Ptex Stats:\n");
printf(" nfilesOpened: %6d\n", nfilesOpened);
printf(" nfilesClosed: %6d\n", nfilesClosed);
printf(" ndataAllocated: %6d\n", ndataAllocated);
printf(" ndataFreed: %6d\n", ndataFreed);
printf(" nblocksRead: %6d\n", nblocksRead);
printf(" nseeks: %6d\n", nseeks);
if (nblocksRead)
printf(" avgReadSize: %6d\n", int(nbytesRead/nblocksRead));
if (nseeks)
printf(" avgSeqReadSize: %6d\n", int(nbytesRead/nseeks));
printf(" MbytesRead: %6.2f\n", nbytesRead/(1024.0*1024.0));
}
}
CacheStats stats;
}
#endif
PtexCacheImpl::~PtexCacheImpl()
{
AutoLockCache locker(cachelock);
while (_unusedData.pop());
while (_unusedFiles.pop());
}
void PtexCacheImpl::setFileInUse(PtexLruItem* file)
{
assert(cachelock.locked());
_unusedFiles.extract(file);
_unusedFileCount--;
}
void PtexCacheImpl::setFileUnused(PtexLruItem* file)
{
assert(cachelock.locked());
_unusedFiles.push(file);
_unusedFileCount++;
}
void PtexCacheImpl::removeFile()
{
_unusedFileCount--;
STATS_INC(nfilesClosed);
}
void PtexCacheImpl::setDataInUse(PtexLruItem*
data,
int size)
{
assert(cachelock.locked());
_unusedData.extract(data);
_unusedDataCount--;
}
void PtexCacheImpl::setDataUnused(PtexLruItem* data, int size)
{
assert(cachelock.locked());
_unusedData.push(data);
_unusedDataCount++;
}
void PtexCacheImpl::removeData(int size) {
_unusedDataCount--;
STATS_INC(ndataFreed);
}
class PtexReaderCache : public PtexCacheImpl
{
public:
PtexReaderCache(int maxFiles, int maxMem, bool premultiply, PtexInputHandler* handler)
: PtexCacheImpl(maxFiles, maxMem),
_io(handler), _cleanupCount(0), _premultiply(premultiply)
{}
~PtexReaderCache()
{
purgeAll();
}
virtual void setSearchPath(const char* path)
{
AutoMutex locker(openlock);
_searchpath = path ? path : "";
_searchdirs.clear();
char* buff = strdup(path);
char* pos = 0;
char* token = strtok_r(buff, ":", &pos);
while (token) {
if (token[0]) _searchdirs.push_back(token);
token = strtok_r(0, ":", &pos);
}
free(buff);
}
virtual const char* getSearchPath()
{
AutoMutex locker(openlock);
return _searchpath.c_str();
}
virtual PtexTexture* get(const char* path, Ptex::String& error);
virtual void purge(PtexTexture*
texture)
{
PtexReader* reader =
dynamic_cast<PtexReader*
>(
texture);
if (!reader) return;
purge(reader->path());
}
virtual void purge(const char* filename)
{
AutoLockCache locker(cachelock);
FileMap::iterator iter = _files.find(filename);
if (iter != _files.end()) {
PtexReader* reader = iter->second;
if (reader && intptr_t(reader) != -1) {
reader->orphan();
iter->second = 0;
}
_files.erase(iter);
}
}
virtual void purgeAll()
{
AutoLockCache locker(cachelock);
FileMap::iterator iter = _files.begin();
while (iter != _files.end()) {
PtexReader* reader = iter->second;
if (reader && intptr_t(reader) != -1) {
reader->orphan();
iter->second = 0;
}
iter = _files.erase(iter);
}
}
void removeBlankEntries()
{
for (FileMap::iterator i = _files.begin(); i != _files.end();) {
if (i->second == 0) i = _files.erase(i);
else i++;
}
}
private:
PtexInputHandler* _io;
std::vector<std::string> _searchdirs;
typedef PtexDict<PtexReader*> FileMap;
FileMap _files;
int _cleanupCount;
bool _premultiply;
};
PtexTexture* PtexReaderCache::get(const char* filename, Ptex::String& error)
{
AutoLockCache locker(cachelock);
PtexReader* reader = _files[filename];
if (reader) {
if (intptr_t(reader) == -1) return 0;
reader->ref();
return reader;
}
else {
bool ok = true;
cachelock.unlock();
AutoMutex openlocker(openlock);
cachelock.lock();
PtexReader** entry = &_files[filename];
if (*entry) {
if (intptr_t(*entry) == -1) return 0;
(*entry)->ref();
return *entry;
}
reader = new PtexReader((void**)entry, this, _premultiply, _io);
cachelock.unlock();
if (!_io) {
char tmppath[PATH_MAX+1];
if (filename[0] != '/' && !_searchdirs.empty()) {
bool found = false;
struct stat statbuf;
for (
size_t i = 0, size = _searchdirs.size(); i <
size; i++) {
snprintf(tmppath, sizeof(tmppath), "%s/%s", _searchdirs[i].c_str(), filename);
if (stat(tmppath, &statbuf) == 0) {
found = true;
filename = tmppath;
break;
}
}
if (!found) {
errstr += filename;
error = errstr.c_str();
ok = false;
}
}
}
if (ok) ok = reader->open(filename, error);
cachelock.lock();
if (!ok) {
*entry = reader;
reader->orphan();
reader->unref();
*entry = (PtexReader*)-1;
return 0;
}
*entry = reader;
purgeFiles();
if (++_cleanupCount >= 1000) {
_cleanupCount = 0;
removeBlankEntries();
}
}
return reader;
}
PtexCache* PtexCache::create(int maxFiles, int maxMem, bool premultiply,
PtexInputHandler* handler)
{
if (maxFiles <= 0) maxFiles = 100;
const int MB = 1024*1024;
if (maxMem <= 0) maxMem = 100 * MB;
if (maxMem < 1 * MB) {
std::cerr <<
"Warning, PtexCache created with < 1 MB" <<
std::endl;
}
return new PtexReaderCache(maxFiles, maxMem, premultiply, handler);
}