1
//---------------------------------------------------------------------------------------
2
// Simple LRU based texture cache
3
//---------------------------------------------------------------------------------------
6
// While it works as it should, it is rather simplistic and not particularly efficient.
7
// Probably a much better approach is to actually allocate a single block of memory,
8
// directly load tiles into it which can be directly accessed.
9
// Usage counts then should be maintained so that no data needs to be moved around.
10
// Problem then is how to organize everything by texture file, tile levels, etc.
21
//---------------------------------------------------------------------------------------
26
static unsigned int cache_total = 0;
27
static unsigned int cache_hit = 0;
28
static unsigned int cache_miss = 0;
29
static size_t texmem_total = 0;
31
TextureCache::TextureCache(size_t memory_maximum) : maxmem(memory_maximum)
33
// set textile_db size to hold as many tiles as the specified memory allows
34
// this is currently based on float RGB tiles of 64x64 only = 4*3*64*64 = 49152 bytes,
35
// so maxtiles is not necessarily the correct number, it depends on the actual data loaded
36
// Should remove the maxitem limit from lrulist and let the code here
37
// signal the list when the cache is full based on 'texmem_total' TODO
38
const size_t maxtiles = (maxmem < 49152) ? 1 : (maxmem / 49152); // at least one tile
39
//cout << "current memory of " << maxmem << " allows " << maxtiles << " color tiles in memory." << endl;
40
textile_db = new lrulist_t<tileID_t, textile_t*>(maxtiles);
41
cache_total = cache_hit = cache_miss = 0;
44
TextureCache::~TextureCache()
46
cout << "cache memory total: " << texmem_total << endl;
47
cout << "cache total : " << cache_total << endl;
48
const float cdv = cache_total ? 100.f/float(cache_total) : 1.f;
49
cout << "cache hit : " << cache_hit << " ( " << cache_hit*cdv << "\% )\n";
50
cout << "cache miss : " << cache_miss << " ( " << cache_miss*cdv << "\% )\n";
52
//cout << "texture_db size = " << texture_db.size() << endl;
53
EXRbuf_t** ei = texture_db.first();
57
ei = texture_db.next();
60
//cout << "deleted " << n << " textures\n";
62
cout << "Total tiles in memory: " << textile_db->size() << endl;
63
textile_t* tt = textile_db->first();
65
delete[] tt->color_data;
67
tt = textile_db->next();
72
// loads new texture if not available yet.
73
// if that failed or some other error occured, will store NULL ptr to prevent the file from trying to be opened continuously
74
const EXRbuf_t* TextureCache::getTextureInfo(const char texname[]) const
76
EXRbuf_t** ei = texture_db.find(texname);
78
// not found, open new .tqd file
79
//char fullpath[512] = {0};
80
//snprintf(fullpath, 512, "%s%s", State::Instance()->topOptions().basepath, texname);
81
EXRbuf_t* ebc = new EXRbuf_t(texname); //fullpath);
83
texture_db.insert(texname, NULL);
87
texture_db.insert(texname, ebc);
91
textile_t* TextureCache::getTile(const tileID_t& tileID) const
94
textile_t** cached_tile = textile_db->find(tileID);
101
// not found, load new tile
102
// possibly the main bottleneck in the code, some sort of prefetching technique might make a difference... TODO
103
textile_t* tt = new textile_t;
104
if (tileID.tex->numChannels() == 1)
105
tt->float_data = tileID.tex->getFloatTile(tileID);
107
tt->color_data = tileID.tex->getColorTile(tileID);
108
if (tt->color_data == NULL) { // is union, so also valid if float_data==NULL
113
texmem_total += tileID.tex->getTileDataSize();
115
// add new tile to texture tile database,
116
// also free data of possibly discarded tile if addition of new tile caused an older one to be thrown out
117
textile_t* discarded_tile = NULL;
118
textile_db->insert(tileID, tt, discarded_tile);
119
if (discarded_tile) {
120
texmem_total -= tileID.tex->getTileDataSize();
121
delete[] discarded_tile->color_data;
122
delete discarded_tile;
129
// returns color at integer coords x,y at level L in 'col', handles all necessary tile updates
130
void TextureCache::getColor(const EXRbuf_t* texinfo, int x, int y, int L, RtColor col) const
132
if (texinfo == NULL) return;
134
const int maxXtile = texinfo->getXTiles(L) - 1, maxYtile = texinfo->getYTiles(L) - 1;
135
const int tilesize = texinfo->getTileXSize(); // mipmap, so always square
137
const int tx = MIN2(int(x / tilesize), maxXtile), ty = MIN2(int(y / tilesize), maxYtile);
138
// tile coords, modulomax w/h not quite correct here yet, depends on wrap modes TODO (same for above tile numbers)
139
x %= tilesize, y %= tilesize;
140
// correction for tiles that overlap the border, only partially valid
141
if (tx == maxXtile) x = MIN2(x, (texinfo->getWidth(L) - 1) % tilesize);
142
if (ty == maxYtile) y = MIN2(y, (texinfo->getHeight(L) - 1) % tilesize);
144
const tileID_t tileID = {texinfo, tx, ty, L, L}; // mipmap for now, so levelX==levelY
145
const textile_t* const tt = getTile(tileID);
148
const unsigned int p = y*tilesize + x;
149
col[0] = tt->color_data[p][0], col[1] = tt->color_data[p][1], col[2] = tt->color_data[p][2];
153
// returns float at integer coords x,y at level L, handles all necessary tile updates
154
float TextureCache::getFloat(const EXRbuf_t* texinfo, int x, int y, int L) const
156
if (texinfo == NULL) return 0.f;
158
const int maxXtile = texinfo->getXTiles(L) - 1, maxYtile = texinfo->getYTiles(L) - 1;
159
const int tilesize = texinfo->getTileXSize(); // mipmap, so always square
161
const int tx = MIN2(int(x / tilesize), maxXtile), ty = MIN2(int(y / tilesize), maxYtile);
162
// tile coords, modulomax w/h not quite correct here yet, depends on wrap modes TODO (same for above tile numbers)
163
x %= tilesize, y %= tilesize;
164
// correction for tiles that overlap the border, only partially valid
165
if (tx == maxXtile) x = MIN2(x, (texinfo->getWidth(L) - 1) % tilesize);
166
if (ty == maxYtile) y = MIN2(y, (texinfo->getHeight(L) - 1) % tilesize);
168
const tileID_t tileID = {texinfo, tx, ty, L, L}; // mipmap for now, so levelX==levelY
169
const textile_t* const tt = getTile(tileID);
172
return tt->float_data[y*tilesize + x];
176
// For use with shadowmaps, returns value at integer coords x,y (always level 0), handles all necessary tile updates
177
// If out of range, returns -1, valid depthvalues are always positive
178
float TextureCache::getDepth(const EXRbuf_t* texinfo, int x, int y) const
180
if ((texinfo == NULL) ||
181
(x < 0) || (y < 0) || (x >= texinfo->getWidth()) || (y >= texinfo->getHeight())) return -1.f; // out of range
182
const int tilesize = texinfo->getTileXSize(); // single level mipmap, always square
183
const tileID_t tileID = {texinfo, MIN2(int(x / tilesize), texinfo->getXTiles() - 1),
184
MIN2(int(y / tilesize), texinfo->getYTiles() - 1), 0, 0};
185
const textile_t* const tt = getTile(tileID);
187
return tt->float_data[(y % tilesize)*tilesize + (x % tilesize)];