5 * Copyright (C) 2007 Hiroshi Morii All Rights Reserved.
6 * Email koolsmoky(at)users.sourceforge.net
7 * Web http://www.3dfxzone.it/koolsmoky
9 * this is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2, or (at your option)
14 * this is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with GNU Make; see the file COPYING. If not, write to
21 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
25 #pragma warning(disable: 4786)
28 #include <boost/filesystem.hpp>
32 #include "../Glide64/m64p.h"
33 #include "../Glide64/Gfx_1.3.h"
37 /* free memory, clean up, etc */
43 TxCache::TxCache(int options, int cachesize, const wchar_t *datapath,
44 const wchar_t *cachepath, const wchar_t *ident,
45 dispInfoFuncExt callback)
47 _txUtil = new TxUtil();
50 _cacheSize = cachesize;
56 _datapath.assign(datapath);
58 _cachepath.assign(cachepath);
64 /* zlib memory buffers to (de)compress hires textures */
65 if (_options & (GZ_TEXCACHE|GZ_HIRESTEXCACHE)) {
66 _gzdest0 = TxMemBuf::getInstance()->get(0);
67 _gzdest1 = TxMemBuf::getInstance()->get(1);
68 _gzdestLen = (TxMemBuf::getInstance()->size_of(0) < TxMemBuf::getInstance()->size_of(1)) ?
69 TxMemBuf::getInstance()->size_of(0) : TxMemBuf::getInstance()->size_of(1);
71 if (!_gzdest0 || !_gzdest1 || !_gzdestLen) {
72 _options &= ~(GZ_TEXCACHE|GZ_HIRESTEXCACHE);
81 TxCache::add(uint64 checksum, GHQTexInfo *info, int dataSize)
83 /* NOTE: dataSize must be provided if info->data is zlib compressed. */
85 if (!checksum || !info->data) return 0;
87 uint8 *dest = info->data;
88 uint16 format = info->format;
91 dataSize = _txUtil->sizeofTx(info->width, info->height, info->format);
93 if (!dataSize) return 0;
95 if (_options & (GZ_TEXCACHE|GZ_HIRESTEXCACHE)) {
96 /* zlib compress it. compression level:1 (best speed) */
97 uLongf destLen = _gzdestLen;
98 dest = (dest == _gzdest0) ? _gzdest1 : _gzdest0;
99 if (compress2(dest, &destLen, info->data, dataSize, 1) != Z_OK) {
101 DBG_INFO(80, L"Error: zlib compression failed!\n");
103 DBG_INFO(80, L"zlib compressed: %.02fkb->%.02fkb\n", (float)dataSize/1000, (float)destLen/1000);
105 format |= GR_TEXFMT_GZ;
110 /* if cache size exceeds limit, remove old cache */
111 if (_cacheSize > 0) {
112 _totalSize += dataSize;
113 if ((_totalSize > _cacheSize) && !_cachelist.empty()) {
114 /* _cachelist is arranged so that frequently used textures are in the back */
115 std::list<uint64>::iterator itList = _cachelist.begin();
116 while (itList != _cachelist.end()) {
117 /* find it in _cache */
118 std::map<uint64, TXCACHE*>::iterator itMap = _cache.find(*itList);
119 if (itMap != _cache.end()) {
120 /* yep we have it. remove it. */
121 _totalSize -= (*itMap).second->size;
122 free((*itMap).second->info.data);
123 delete (*itMap).second;
128 /* check if memory cache has enough space */
129 if (_totalSize <= _cacheSize)
132 /* remove from _cachelist */
133 _cachelist.erase(_cachelist.begin(), itList);
135 DBG_INFO(80, L"+++++++++\n");
137 _totalSize -= dataSize;
141 uint8 *tmpdata = (uint8*)malloc(dataSize);
143 TXCACHE *txCache = new TXCACHE;
145 /* we can directly write as we filter, but for now we get away
146 * with doing memcpy after all the filtering is done.
148 memcpy(tmpdata, dest, dataSize);
151 memcpy(&txCache->info, info, sizeof(GHQTexInfo));
152 txCache->info.data = tmpdata;
153 txCache->info.format = format;
154 txCache->size = dataSize;
157 if (_cacheSize > 0) {
158 _cachelist.push_back(checksum);
159 txCache->it = --(_cachelist.end());
161 /* _cache[checksum] = txCache; */
162 _cache.insert(std::map<uint64, TXCACHE*>::value_type(checksum, txCache));
165 DBG_INFO(80, L"[%5d] added!! crc:%08X %08X %d x %d gfmt:%x total:%.02fmb\n",
166 _cache.size(), (uint32)(checksum >> 32), (uint32)(checksum & 0xffffffff),
167 info->width, info->height, info->format, (float)_totalSize/1000000);
169 DBG_INFO(80, L"smalllodlog2:%d largelodlog2:%d aspectratiolog2:%d\n",
170 txCache->info.smallLodLog2, txCache->info.largeLodLog2, txCache->info.aspectRatioLog2);
173 DBG_INFO(80, L"tiles:%d un-tiled size:%d x %d\n", info->tiles, info->untiled_width, info->untiled_height);
176 if (_cacheSize > 0) {
177 DBG_INFO(80, L"cache max config:%.02fmb\n", (float)_cacheSize/1000000);
179 if (_cache.size() != _cachelist.size()) {
180 DBG_INFO(80, L"Error: cache/cachelist mismatch! (%d/%d)\n", _cache.size(), _cachelist.size());
185 /* total cache size */
186 _totalSize += dataSize;
197 TxCache::get(uint64 checksum, GHQTexInfo *info)
199 if (!checksum || _cache.empty()) return 0;
201 /* find a match in cache */
202 std::map<uint64, TXCACHE*>::iterator itMap = _cache.find(checksum);
203 if (itMap != _cache.end()) {
204 /* yep, we've got it. */
205 memcpy(info, &(((*itMap).second)->info), sizeof(GHQTexInfo));
207 /* push it to the back of the list */
208 if (_cacheSize > 0) {
209 _cachelist.erase(((*itMap).second)->it);
210 _cachelist.push_back(checksum);
211 ((*itMap).second)->it = --(_cachelist.end());
214 /* zlib decompress it */
215 if (info->format & GR_TEXFMT_GZ) {
216 uLongf destLen = _gzdestLen;
217 uint8 *dest = (_gzdest0 == info->data) ? _gzdest1 : _gzdest0;
218 if (uncompress(dest, &destLen, info->data, ((*itMap).second)->size) != Z_OK) {
219 DBG_INFO(80, L"Error: zlib decompression failed!\n");
223 info->format &= ~GR_TEXFMT_GZ;
224 DBG_INFO(80, L"zlib decompressed: %.02fkb->%.02fkb\n", (float)(((*itMap).second)->size)/1000, (float)destLen/1000);
234 TxCache::save(const wchar_t *path, const wchar_t *filename, int config)
236 if (!_cache.empty()) {
237 /* dump cache to disk */
240 boost::filesystem::wpath cachepath(path);
241 boost::filesystem::create_directory(cachepath);
243 /* Ugly hack to enable fopen/gzopen in Win9x */
244 #ifdef BOOST_WINDOWS_API
245 wchar_t curpath[MAX_PATH];
246 GETCWD(MAX_PATH, curpath);
247 CHDIR(cachepath.wstring().c_str());
249 char curpath[MAX_PATH];
250 wcstombs(cbuf, cachepath.wstring().c_str(), MAX_PATH);
251 if (GETCWD(MAX_PATH, curpath) == NULL)
252 ERRLOG("Error while retrieving working directory!");
253 if (CHDIR(cbuf) != 0)
254 ERRLOG("Error while changing current directory to '%s'!", cbuf);
257 wcstombs(cbuf, filename, MAX_PATH);
259 gzFile gzfp = gzopen(cbuf, "wb1");
260 DBG_INFO(80, L"gzfp:%x file:%ls\n", gzfp, filename);
262 /* write header to determine config match */
263 gzwrite(gzfp, &config, 4);
265 std::map<uint64, TXCACHE*>::iterator itMap = _cache.begin();
266 while (itMap != _cache.end()) {
267 uint8 *dest = (*itMap).second->info.data;
268 uint32 destLen = (*itMap).second->size;
269 uint16 format = (*itMap).second->info.format;
271 /* to keep things simple, we save the texture data in a zlib uncompressed state. */
272 /* sigh... for those who cannot wait the extra few seconds. changed to keep
273 * texture data in a zlib compressed state. if the GZ_TEXCACHE or GZ_HIRESTEXCACHE
274 * option is toggled, the cache will need to be rebuilt.
276 /*if (format & GR_TEXFMT_GZ) {
278 destLen = _gzdestLen;
279 if (dest && destLen) {
280 if (uncompress(dest, &destLen, (*itMap).second->info.data, (*itMap).second->size) != Z_OK) {
284 format &= ~GR_TEXFMT_GZ;
288 if (dest && destLen) {
289 /* texture checksum */
290 gzwrite(gzfp, &((*itMap).first), 8);
292 /* other texture info */
293 gzwrite(gzfp, &((*itMap).second->info.width), 4);
294 gzwrite(gzfp, &((*itMap).second->info.height), 4);
295 gzwrite(gzfp, &format, 2);
297 gzwrite(gzfp, &((*itMap).second->info.smallLodLog2), 4);
298 gzwrite(gzfp, &((*itMap).second->info.largeLodLog2), 4);
299 gzwrite(gzfp, &((*itMap).second->info.aspectRatioLog2), 4);
301 gzwrite(gzfp, &((*itMap).second->info.tiles), 4);
302 gzwrite(gzfp, &((*itMap).second->info.untiled_width), 4);
303 gzwrite(gzfp, &((*itMap).second->info.untiled_height), 4);
305 gzwrite(gzfp, &((*itMap).second->info.is_hires_tex), 1);
307 gzwrite(gzfp, &destLen, 4);
308 gzwrite(gzfp, dest, destLen);
315 (*_callback)(L"Total textures saved to HDD: %d\n", std::distance(itMap, _cache.begin()));*/
320 if (CHDIR(curpath) != 0)
321 ERRLOG("Error while changing current directory back to original path of '%s'!", curpath);
324 return _cache.empty();
328 TxCache::load(const wchar_t *path, const wchar_t *filename, int config)
330 /* find it on disk */
333 boost::filesystem::wpath cachepath(path);
335 #ifdef BOOST_WINDOWS_API
336 wchar_t curpath[MAX_PATH];
337 GETCWD(MAX_PATH, curpath);
338 CHDIR(cachepath.wstring().c_str());
340 char curpath[MAX_PATH];
341 wcstombs(cbuf, cachepath.wstring().c_str(), MAX_PATH);
342 if (GETCWD(MAX_PATH, curpath) == NULL)
343 ERRLOG("Error while retrieving working directory!");
344 if (CHDIR(cbuf) != 0)
345 ERRLOG("Error while changing current directory to '%s'!", cbuf);
348 wcstombs(cbuf, filename, MAX_PATH);
350 gzFile gzfp = gzopen(cbuf, "rb");
351 DBG_INFO(80, L"gzfp:%x file:%ls\n", gzfp, filename);
353 /* yep, we have it. load it into memory cache. */
358 /* read header to determine config match */
359 gzread(gzfp, &tmpconfig, 4);
361 if (tmpconfig == config) {
363 memset(&tmpInfo, 0, sizeof(GHQTexInfo));
365 gzread(gzfp, &checksum, 8);
367 gzread(gzfp, &tmpInfo.width, 4);
368 gzread(gzfp, &tmpInfo.height, 4);
369 gzread(gzfp, &tmpInfo.format, 2);
371 gzread(gzfp, &tmpInfo.smallLodLog2, 4);
372 gzread(gzfp, &tmpInfo.largeLodLog2, 4);
373 gzread(gzfp, &tmpInfo.aspectRatioLog2, 4);
375 gzread(gzfp, &tmpInfo.tiles, 4);
376 gzread(gzfp, &tmpInfo.untiled_width, 4);
377 gzread(gzfp, &tmpInfo.untiled_height, 4);
379 gzread(gzfp, &tmpInfo.is_hires_tex, 1);
381 gzread(gzfp, &dataSize, 4);
383 tmpInfo.data = (uint8*)malloc(dataSize);
385 gzread(gzfp, tmpInfo.data, dataSize);
387 /* add to memory cache */
388 add(checksum, &tmpInfo, (tmpInfo.format & GR_TEXFMT_GZ) ? dataSize : 0);
392 gzseek(gzfp, dataSize, SEEK_CUR);
395 /* skip in between to prevent the loop from being tied down to vsync */
396 if (_callback && (!(_cache.size() % 100) || gzeof(gzfp)))
397 (*_callback)(L"[%d] total mem:%.02fmb - %ls\n", _cache.size(), (float)_totalSize/1000000, filename);
399 } while (!gzeof(gzfp));
402 if ((tmpconfig & HIRESTEXTURES_MASK) != (config & HIRESTEXTURES_MASK)) {
403 const char *conf_str;
404 if ((tmpconfig & HIRESTEXTURES_MASK) == NO_HIRESTEXTURES)
406 else if ((tmpconfig & HIRESTEXTURES_MASK) == RICE_HIRESTEXTURES)
409 conf_str = "set to an unsupported format";
411 WriteLog(M64MSG_WARNING, "Ignored texture cache due to incompatible setting: ghq_hirs must be %s", conf_str);
413 if ((tmpconfig & COMPRESS_HIRESTEX) != (config & COMPRESS_HIRESTEX))
414 WriteLog(M64MSG_WARNING, "Ignored texture cache due to incompatible setting: ghq_hirs_cmpr must be %s", (tmpconfig & COMPRESS_HIRESTEX) ? "True" : "False");
415 if ((tmpconfig & COMPRESSION_MASK) != (config & COMPRESSION_MASK) && (tmpconfig & COMPRESS_HIRESTEX)) {
416 const char *conf_str;
417 if ((tmpconfig & COMPRESSION_MASK) == FXT1_COMPRESSION)
419 else if ((tmpconfig & COMPRESSION_MASK) == S3TC_COMPRESSION)
422 conf_str = "set to an unsupported format";
424 WriteLog(M64MSG_WARNING, "Ignored texture cache due to incompatible setting: ghq_cmpr must be %s", conf_str);
426 if ((tmpconfig & TILE_HIRESTEX) != (config & TILE_HIRESTEX))
427 WriteLog(M64MSG_WARNING, "Ignored texture cache due to incompatible setting: ghq_hirs_tile must be %s", (tmpconfig & TILE_HIRESTEX) ? "True" : "False");
428 if ((tmpconfig & FORCE16BPP_HIRESTEX) != (config & FORCE16BPP_HIRESTEX))
429 WriteLog(M64MSG_WARNING, "Ignored texture cache due to incompatible setting: ghq_hirs_f16bpp must be %s", (tmpconfig & FORCE16BPP_HIRESTEX) ? "True" : "False");
430 if ((tmpconfig & GZ_HIRESTEXCACHE) != (config & GZ_HIRESTEXCACHE))
431 WriteLog(M64MSG_WARNING, "ghq_hirs_gz must be %s", (tmpconfig & GZ_HIRESTEXCACHE) ? "True" : "False");
432 if ((tmpconfig & LET_TEXARTISTS_FLY) != (config & LET_TEXARTISTS_FLY))
433 WriteLog(M64MSG_WARNING, "Ignored texture cache due to incompatible setting: ghq_hirs_let_texartists_fly must be %s", (tmpconfig & LET_TEXARTISTS_FLY) ? "True" : "False");
435 if ((tmpconfig & FILTER_MASK) != (config & FILTER_MASK)) {
436 const char *conf_str;
437 if ((tmpconfig & FILTER_MASK) == NO_FILTER)
439 else if ((tmpconfig & FILTER_MASK) == SMOOTH_FILTER_1)
441 else if ((tmpconfig & FILTER_MASK) == SMOOTH_FILTER_2)
443 else if ((tmpconfig & FILTER_MASK) == SMOOTH_FILTER_3)
445 else if ((tmpconfig & FILTER_MASK) == SMOOTH_FILTER_4)
447 else if ((tmpconfig & FILTER_MASK) == SHARP_FILTER_1)
449 else if ((tmpconfig & FILTER_MASK) == SHARP_FILTER_2)
452 conf_str = "set to an unsupported format";
453 WriteLog(M64MSG_WARNING, "Ignored texture cache due to incompatible setting: ghq_fltr must be %s", conf_str);
456 if ((tmpconfig & ENHANCEMENT_MASK) != (config & ENHANCEMENT_MASK)) {
457 const char *conf_str;
458 if ((tmpconfig & ENHANCEMENT_MASK) == NO_ENHANCEMENT)
460 else if ((tmpconfig & ENHANCEMENT_MASK) == X2_ENHANCEMENT)
462 else if ((tmpconfig & ENHANCEMENT_MASK) == X2SAI_ENHANCEMENT)
464 else if ((tmpconfig & ENHANCEMENT_MASK) == HQ2X_ENHANCEMENT)
466 else if ((tmpconfig & ENHANCEMENT_MASK) == HQ2XS_ENHANCEMENT)
468 else if ((tmpconfig & ENHANCEMENT_MASK) == LQ2X_ENHANCEMENT)
470 else if ((tmpconfig & ENHANCEMENT_MASK) == LQ2XS_ENHANCEMENT)
472 else if ((tmpconfig & ENHANCEMENT_MASK) == HQ4X_ENHANCEMENT)
475 conf_str = "set to an unsupported format";
476 WriteLog(M64MSG_WARNING, "Ignored texture cache due to incompatible setting: ghq_enht must be %s", conf_str);
479 if ((tmpconfig & COMPRESS_TEX) != (config & COMPRESS_TEX))
480 WriteLog(M64MSG_WARNING, "Ignored texture cache due to incompatible setting: ghq_enht_cmpr must be %s", (tmpconfig & COMPRESS_TEX) ? "True" : "False");
481 if ((tmpconfig & FORCE16BPP_TEX) != (config & FORCE16BPP_TEX))
482 WriteLog(M64MSG_WARNING, "Ignored texture cache due to incompatible setting: ghq_enht_f16bpp must be %s", (tmpconfig & FORCE16BPP_TEX) ? "True" : "False");
483 if ((tmpconfig & GZ_TEXCACHE) != (config & GZ_TEXCACHE))
484 WriteLog(M64MSG_WARNING, "Ignored texture cache due to incompatible setting: ghq_enht_gz must be %s", (tmpconfig & GZ_TEXCACHE) ? "True" : "False");
488 if (CHDIR(curpath) != 0)
489 ERRLOG("Error while changing current directory back to original path of '%s'!", curpath);
491 return !_cache.empty();
495 TxCache::del(uint64 checksum)
497 if (!checksum || _cache.empty()) return 0;
499 std::map<uint64, TXCACHE*>::iterator itMap = _cache.find(checksum);
500 if (itMap != _cache.end()) {
502 /* for texture cache (not hi-res cache) */
503 if (!_cachelist.empty()) _cachelist.erase(((*itMap).second)->it);
505 /* remove from cache */
506 free((*itMap).second->info.data);
507 _totalSize -= (*itMap).second->size;
508 delete (*itMap).second;
511 DBG_INFO(80, L"removed from cache: checksum = %08X %08X\n", (uint32)(checksum & 0xffffffff), (uint32)(checksum >> 32));
520 TxCache::is_cached(uint64 checksum)
522 std::map<uint64, TXCACHE*>::iterator itMap = _cache.find(checksum);
523 if (itMap != _cache.end()) return 1;
531 if (!_cache.empty()) {
532 std::map<uint64, TXCACHE*>::iterator itMap = _cache.begin();
533 while (itMap != _cache.end()) {
534 free((*itMap).second->info.data);
535 delete (*itMap).second;
541 if (!_cachelist.empty()) _cachelist.clear();