X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=source%2Fgles2glide64%2Fsrc%2FGlideHQ%2FTxCache.cpp;fp=source%2Fgles2glide64%2Fsrc%2FGlideHQ%2FTxCache.cpp;h=46960f4d463e71e827a6d9a255c3b793f380c6d9;hb=98e75f2d18c02c233da543560f76282f04fc796c;hp=0000000000000000000000000000000000000000;hpb=0ced54f867d36e8b324155bef49e8abfebfc3237;p=mupen64plus-pandora.git diff --git a/source/gles2glide64/src/GlideHQ/TxCache.cpp b/source/gles2glide64/src/GlideHQ/TxCache.cpp new file mode 100644 index 0000000..46960f4 --- /dev/null +++ b/source/gles2glide64/src/GlideHQ/TxCache.cpp @@ -0,0 +1,544 @@ +/* + * Texture Filtering + * Version: 1.0 + * + * Copyright (C) 2007 Hiroshi Morii All Rights Reserved. + * Email koolsmoky(at)users.sourceforge.net + * Web http://www.3dfxzone.it/koolsmoky + * + * this is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * this is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifdef __MSC__ +#pragma warning(disable: 4786) +#endif + +#include +#include +#include "TxCache.h" +#include "TxDbg.h" +#include "../Glide64/m64p.h" +#include "../Glide64/Gfx_1.3.h" + +TxCache::~TxCache() +{ + /* free memory, clean up, etc */ + clear(); + + delete _txUtil; +} + +TxCache::TxCache(int options, int cachesize, const wchar_t *datapath, + const wchar_t *cachepath, const wchar_t *ident, + dispInfoFuncExt callback) +{ + _txUtil = new TxUtil(); + + _options = options; + _cacheSize = cachesize; + _callback = callback; + _totalSize = 0; + + /* save path name */ + if (datapath) + _datapath.assign(datapath); + if (cachepath) + _cachepath.assign(cachepath); + + /* save ROM name */ + if (ident) + _ident.assign(ident); + + /* zlib memory buffers to (de)compress hires textures */ + if (_options & (GZ_TEXCACHE|GZ_HIRESTEXCACHE)) { + _gzdest0 = TxMemBuf::getInstance()->get(0); + _gzdest1 = TxMemBuf::getInstance()->get(1); + _gzdestLen = (TxMemBuf::getInstance()->size_of(0) < TxMemBuf::getInstance()->size_of(1)) ? + TxMemBuf::getInstance()->size_of(0) : TxMemBuf::getInstance()->size_of(1); + + if (!_gzdest0 || !_gzdest1 || !_gzdestLen) { + _options &= ~(GZ_TEXCACHE|GZ_HIRESTEXCACHE); + _gzdest0 = NULL; + _gzdest1 = NULL; + _gzdestLen = 0; + } + } +} + +boolean +TxCache::add(uint64 checksum, GHQTexInfo *info, int dataSize) +{ + /* NOTE: dataSize must be provided if info->data is zlib compressed. */ + + if (!checksum || !info->data) return 0; + + uint8 *dest = info->data; + uint16 format = info->format; + + if (!dataSize) { + dataSize = _txUtil->sizeofTx(info->width, info->height, info->format); + + if (!dataSize) return 0; + + if (_options & (GZ_TEXCACHE|GZ_HIRESTEXCACHE)) { + /* zlib compress it. compression level:1 (best speed) */ + uLongf destLen = _gzdestLen; + dest = (dest == _gzdest0) ? _gzdest1 : _gzdest0; + if (compress2(dest, &destLen, info->data, dataSize, 1) != Z_OK) { + dest = info->data; + DBG_INFO(80, L"Error: zlib compression failed!\n"); + } else { + DBG_INFO(80, L"zlib compressed: %.02fkb->%.02fkb\n", (float)dataSize/1000, (float)destLen/1000); + dataSize = destLen; + format |= GR_TEXFMT_GZ; + } + } + } + + /* if cache size exceeds limit, remove old cache */ + if (_cacheSize > 0) { + _totalSize += dataSize; + if ((_totalSize > _cacheSize) && !_cachelist.empty()) { + /* _cachelist is arranged so that frequently used textures are in the back */ + std::list::iterator itList = _cachelist.begin(); + while (itList != _cachelist.end()) { + /* find it in _cache */ + std::map::iterator itMap = _cache.find(*itList); + if (itMap != _cache.end()) { + /* yep we have it. remove it. */ + _totalSize -= (*itMap).second->size; + free((*itMap).second->info.data); + delete (*itMap).second; + _cache.erase(itMap); + } + itList++; + + /* check if memory cache has enough space */ + if (_totalSize <= _cacheSize) + break; + } + /* remove from _cachelist */ + _cachelist.erase(_cachelist.begin(), itList); + + DBG_INFO(80, L"+++++++++\n"); + } + _totalSize -= dataSize; + } + + /* cache it */ + uint8 *tmpdata = (uint8*)malloc(dataSize); + if (tmpdata) { + TXCACHE *txCache = new TXCACHE; + if (txCache) { + /* we can directly write as we filter, but for now we get away + * with doing memcpy after all the filtering is done. + */ + memcpy(tmpdata, dest, dataSize); + + /* copy it */ + memcpy(&txCache->info, info, sizeof(GHQTexInfo)); + txCache->info.data = tmpdata; + txCache->info.format = format; + txCache->size = dataSize; + + /* add to cache */ + if (_cacheSize > 0) { + _cachelist.push_back(checksum); + txCache->it = --(_cachelist.end()); + } + /* _cache[checksum] = txCache; */ + _cache.insert(std::map::value_type(checksum, txCache)); + +#ifdef DEBUG + DBG_INFO(80, L"[%5d] added!! crc:%08X %08X %d x %d gfmt:%x total:%.02fmb\n", + _cache.size(), (uint32)(checksum >> 32), (uint32)(checksum & 0xffffffff), + info->width, info->height, info->format, (float)_totalSize/1000000); + + DBG_INFO(80, L"smalllodlog2:%d largelodlog2:%d aspectratiolog2:%d\n", + txCache->info.smallLodLog2, txCache->info.largeLodLog2, txCache->info.aspectRatioLog2); + + if (info->tiles) { + DBG_INFO(80, L"tiles:%d un-tiled size:%d x %d\n", info->tiles, info->untiled_width, info->untiled_height); + } + + if (_cacheSize > 0) { + DBG_INFO(80, L"cache max config:%.02fmb\n", (float)_cacheSize/1000000); + + if (_cache.size() != _cachelist.size()) { + DBG_INFO(80, L"Error: cache/cachelist mismatch! (%d/%d)\n", _cache.size(), _cachelist.size()); + } + } +#endif + + /* total cache size */ + _totalSize += dataSize; + + return 1; + } + free(tmpdata); + } + + return 0; +} + +boolean +TxCache::get(uint64 checksum, GHQTexInfo *info) +{ + if (!checksum || _cache.empty()) return 0; + + /* find a match in cache */ + std::map::iterator itMap = _cache.find(checksum); + if (itMap != _cache.end()) { + /* yep, we've got it. */ + memcpy(info, &(((*itMap).second)->info), sizeof(GHQTexInfo)); + + /* push it to the back of the list */ + if (_cacheSize > 0) { + _cachelist.erase(((*itMap).second)->it); + _cachelist.push_back(checksum); + ((*itMap).second)->it = --(_cachelist.end()); + } + + /* zlib decompress it */ + if (info->format & GR_TEXFMT_GZ) { + uLongf destLen = _gzdestLen; + uint8 *dest = (_gzdest0 == info->data) ? _gzdest1 : _gzdest0; + if (uncompress(dest, &destLen, info->data, ((*itMap).second)->size) != Z_OK) { + DBG_INFO(80, L"Error: zlib decompression failed!\n"); + return 0; + } + info->data = dest; + info->format &= ~GR_TEXFMT_GZ; + DBG_INFO(80, L"zlib decompressed: %.02fkb->%.02fkb\n", (float)(((*itMap).second)->size)/1000, (float)destLen/1000); + } + + return 1; + } + + return 0; +} + +boolean +TxCache::save(const wchar_t *path, const wchar_t *filename, int config) +{ + if (!_cache.empty()) { + /* dump cache to disk */ + char cbuf[MAX_PATH]; + + boost::filesystem::wpath cachepath(path); + boost::filesystem::create_directory(cachepath); + + /* Ugly hack to enable fopen/gzopen in Win9x */ +#ifdef BOOST_WINDOWS_API + wchar_t curpath[MAX_PATH]; + GETCWD(MAX_PATH, curpath); + CHDIR(cachepath.wstring().c_str()); +#else + char curpath[MAX_PATH]; + wcstombs(cbuf, cachepath.wstring().c_str(), MAX_PATH); + if (GETCWD(MAX_PATH, curpath) == NULL) + ERRLOG("Error while retrieving working directory!"); + if (CHDIR(cbuf) != 0) + ERRLOG("Error while changing current directory to '%s'!", cbuf); +#endif + + wcstombs(cbuf, filename, MAX_PATH); + + gzFile gzfp = gzopen(cbuf, "wb1"); + DBG_INFO(80, L"gzfp:%x file:%ls\n", gzfp, filename); + if (gzfp) { + /* write header to determine config match */ + gzwrite(gzfp, &config, 4); + + std::map::iterator itMap = _cache.begin(); + while (itMap != _cache.end()) { + uint8 *dest = (*itMap).second->info.data; + uint32 destLen = (*itMap).second->size; + uint16 format = (*itMap).second->info.format; + + /* to keep things simple, we save the texture data in a zlib uncompressed state. */ + /* sigh... for those who cannot wait the extra few seconds. changed to keep + * texture data in a zlib compressed state. if the GZ_TEXCACHE or GZ_HIRESTEXCACHE + * option is toggled, the cache will need to be rebuilt. + */ + /*if (format & GR_TEXFMT_GZ) { + dest = _gzdest0; + destLen = _gzdestLen; + if (dest && destLen) { + if (uncompress(dest, &destLen, (*itMap).second->info.data, (*itMap).second->size) != Z_OK) { + dest = NULL; + destLen = 0; + } + format &= ~GR_TEXFMT_GZ; + } + }*/ + + if (dest && destLen) { + /* texture checksum */ + gzwrite(gzfp, &((*itMap).first), 8); + + /* other texture info */ + gzwrite(gzfp, &((*itMap).second->info.width), 4); + gzwrite(gzfp, &((*itMap).second->info.height), 4); + gzwrite(gzfp, &format, 2); + + gzwrite(gzfp, &((*itMap).second->info.smallLodLog2), 4); + gzwrite(gzfp, &((*itMap).second->info.largeLodLog2), 4); + gzwrite(gzfp, &((*itMap).second->info.aspectRatioLog2), 4); + + gzwrite(gzfp, &((*itMap).second->info.tiles), 4); + gzwrite(gzfp, &((*itMap).second->info.untiled_width), 4); + gzwrite(gzfp, &((*itMap).second->info.untiled_height), 4); + + gzwrite(gzfp, &((*itMap).second->info.is_hires_tex), 1); + + gzwrite(gzfp, &destLen, 4); + gzwrite(gzfp, dest, destLen); + } + + itMap++; + + /* not ready yet */ + /*if (_callback) + (*_callback)(L"Total textures saved to HDD: %d\n", std::distance(itMap, _cache.begin()));*/ + } + gzclose(gzfp); + } + + if (CHDIR(curpath) != 0) + ERRLOG("Error while changing current directory back to original path of '%s'!", curpath); + } + + return _cache.empty(); +} + +boolean +TxCache::load(const wchar_t *path, const wchar_t *filename, int config) +{ + /* find it on disk */ + char cbuf[MAX_PATH]; + + boost::filesystem::wpath cachepath(path); + +#ifdef BOOST_WINDOWS_API + wchar_t curpath[MAX_PATH]; + GETCWD(MAX_PATH, curpath); + CHDIR(cachepath.wstring().c_str()); +#else + char curpath[MAX_PATH]; + wcstombs(cbuf, cachepath.wstring().c_str(), MAX_PATH); + if (GETCWD(MAX_PATH, curpath) == NULL) + ERRLOG("Error while retrieving working directory!"); + if (CHDIR(cbuf) != 0) + ERRLOG("Error while changing current directory to '%s'!", cbuf); +#endif + + wcstombs(cbuf, filename, MAX_PATH); + + gzFile gzfp = gzopen(cbuf, "rb"); + DBG_INFO(80, L"gzfp:%x file:%ls\n", gzfp, filename); + if (gzfp) { + /* yep, we have it. load it into memory cache. */ + int dataSize; + uint64 checksum; + GHQTexInfo tmpInfo; + int tmpconfig; + /* read header to determine config match */ + gzread(gzfp, &tmpconfig, 4); + + if (tmpconfig == config) { + do { + memset(&tmpInfo, 0, sizeof(GHQTexInfo)); + + gzread(gzfp, &checksum, 8); + + gzread(gzfp, &tmpInfo.width, 4); + gzread(gzfp, &tmpInfo.height, 4); + gzread(gzfp, &tmpInfo.format, 2); + + gzread(gzfp, &tmpInfo.smallLodLog2, 4); + gzread(gzfp, &tmpInfo.largeLodLog2, 4); + gzread(gzfp, &tmpInfo.aspectRatioLog2, 4); + + gzread(gzfp, &tmpInfo.tiles, 4); + gzread(gzfp, &tmpInfo.untiled_width, 4); + gzread(gzfp, &tmpInfo.untiled_height, 4); + + gzread(gzfp, &tmpInfo.is_hires_tex, 1); + + gzread(gzfp, &dataSize, 4); + + tmpInfo.data = (uint8*)malloc(dataSize); + if (tmpInfo.data) { + gzread(gzfp, tmpInfo.data, dataSize); + + /* add to memory cache */ + add(checksum, &tmpInfo, (tmpInfo.format & GR_TEXFMT_GZ) ? dataSize : 0); + + free(tmpInfo.data); + } else { + gzseek(gzfp, dataSize, SEEK_CUR); + } + + /* skip in between to prevent the loop from being tied down to vsync */ + if (_callback && (!(_cache.size() % 100) || gzeof(gzfp))) + (*_callback)(L"[%d] total mem:%.02fmb - %ls\n", _cache.size(), (float)_totalSize/1000000, filename); + + } while (!gzeof(gzfp)); + gzclose(gzfp); + } else { + if ((tmpconfig & HIRESTEXTURES_MASK) != (config & HIRESTEXTURES_MASK)) { + const char *conf_str; + if ((tmpconfig & HIRESTEXTURES_MASK) == NO_HIRESTEXTURES) + conf_str = "0"; + else if ((tmpconfig & HIRESTEXTURES_MASK) == RICE_HIRESTEXTURES) + conf_str = "1"; + else + conf_str = "set to an unsupported format"; + + WriteLog(M64MSG_WARNING, "Ignored texture cache due to incompatible setting: ghq_hirs must be %s", conf_str); + } + if ((tmpconfig & COMPRESS_HIRESTEX) != (config & COMPRESS_HIRESTEX)) + WriteLog(M64MSG_WARNING, "Ignored texture cache due to incompatible setting: ghq_hirs_cmpr must be %s", (tmpconfig & COMPRESS_HIRESTEX) ? "True" : "False"); + if ((tmpconfig & COMPRESSION_MASK) != (config & COMPRESSION_MASK) && (tmpconfig & COMPRESS_HIRESTEX)) { + const char *conf_str; + if ((tmpconfig & COMPRESSION_MASK) == FXT1_COMPRESSION) + conf_str = "1"; + else if ((tmpconfig & COMPRESSION_MASK) == S3TC_COMPRESSION) + conf_str = "0"; + else + conf_str = "set to an unsupported format"; + + WriteLog(M64MSG_WARNING, "Ignored texture cache due to incompatible setting: ghq_cmpr must be %s", conf_str); + } + if ((tmpconfig & TILE_HIRESTEX) != (config & TILE_HIRESTEX)) + WriteLog(M64MSG_WARNING, "Ignored texture cache due to incompatible setting: ghq_hirs_tile must be %s", (tmpconfig & TILE_HIRESTEX) ? "True" : "False"); + if ((tmpconfig & FORCE16BPP_HIRESTEX) != (config & FORCE16BPP_HIRESTEX)) + WriteLog(M64MSG_WARNING, "Ignored texture cache due to incompatible setting: ghq_hirs_f16bpp must be %s", (tmpconfig & FORCE16BPP_HIRESTEX) ? "True" : "False"); + if ((tmpconfig & GZ_HIRESTEXCACHE) != (config & GZ_HIRESTEXCACHE)) + WriteLog(M64MSG_WARNING, "ghq_hirs_gz must be %s", (tmpconfig & GZ_HIRESTEXCACHE) ? "True" : "False"); + if ((tmpconfig & LET_TEXARTISTS_FLY) != (config & LET_TEXARTISTS_FLY)) + WriteLog(M64MSG_WARNING, "Ignored texture cache due to incompatible setting: ghq_hirs_let_texartists_fly must be %s", (tmpconfig & LET_TEXARTISTS_FLY) ? "True" : "False"); + + if ((tmpconfig & FILTER_MASK) != (config & FILTER_MASK)) { + const char *conf_str; + if ((tmpconfig & FILTER_MASK) == NO_FILTER) + conf_str = "0"; + else if ((tmpconfig & FILTER_MASK) == SMOOTH_FILTER_1) + conf_str = "1"; + else if ((tmpconfig & FILTER_MASK) == SMOOTH_FILTER_2) + conf_str = "2"; + else if ((tmpconfig & FILTER_MASK) == SMOOTH_FILTER_3) + conf_str = "3"; + else if ((tmpconfig & FILTER_MASK) == SMOOTH_FILTER_4) + conf_str = "4"; + else if ((tmpconfig & FILTER_MASK) == SHARP_FILTER_1) + conf_str = "5"; + else if ((tmpconfig & FILTER_MASK) == SHARP_FILTER_2) + conf_str = "6"; + else + conf_str = "set to an unsupported format"; + WriteLog(M64MSG_WARNING, "Ignored texture cache due to incompatible setting: ghq_fltr must be %s", conf_str); + } + + if ((tmpconfig & ENHANCEMENT_MASK) != (config & ENHANCEMENT_MASK)) { + const char *conf_str; + if ((tmpconfig & ENHANCEMENT_MASK) == NO_ENHANCEMENT) + conf_str = "0"; + else if ((tmpconfig & ENHANCEMENT_MASK) == X2_ENHANCEMENT) + conf_str = "2"; + else if ((tmpconfig & ENHANCEMENT_MASK) == X2SAI_ENHANCEMENT) + conf_str = "3"; + else if ((tmpconfig & ENHANCEMENT_MASK) == HQ2X_ENHANCEMENT) + conf_str = "4"; + else if ((tmpconfig & ENHANCEMENT_MASK) == HQ2XS_ENHANCEMENT) + conf_str = "5"; + else if ((tmpconfig & ENHANCEMENT_MASK) == LQ2X_ENHANCEMENT) + conf_str = "6"; + else if ((tmpconfig & ENHANCEMENT_MASK) == LQ2XS_ENHANCEMENT) + conf_str = "7"; + else if ((tmpconfig & ENHANCEMENT_MASK) == HQ4X_ENHANCEMENT) + conf_str = "8"; + else + conf_str = "set to an unsupported format"; + WriteLog(M64MSG_WARNING, "Ignored texture cache due to incompatible setting: ghq_enht must be %s", conf_str); + } + + if ((tmpconfig & COMPRESS_TEX) != (config & COMPRESS_TEX)) + WriteLog(M64MSG_WARNING, "Ignored texture cache due to incompatible setting: ghq_enht_cmpr must be %s", (tmpconfig & COMPRESS_TEX) ? "True" : "False"); + if ((tmpconfig & FORCE16BPP_TEX) != (config & FORCE16BPP_TEX)) + WriteLog(M64MSG_WARNING, "Ignored texture cache due to incompatible setting: ghq_enht_f16bpp must be %s", (tmpconfig & FORCE16BPP_TEX) ? "True" : "False"); + if ((tmpconfig & GZ_TEXCACHE) != (config & GZ_TEXCACHE)) + WriteLog(M64MSG_WARNING, "Ignored texture cache due to incompatible setting: ghq_enht_gz must be %s", (tmpconfig & GZ_TEXCACHE) ? "True" : "False"); + } + } + + if (CHDIR(curpath) != 0) + ERRLOG("Error while changing current directory back to original path of '%s'!", curpath); + + return !_cache.empty(); +} + +boolean +TxCache::del(uint64 checksum) +{ + if (!checksum || _cache.empty()) return 0; + + std::map::iterator itMap = _cache.find(checksum); + if (itMap != _cache.end()) { + + /* for texture cache (not hi-res cache) */ + if (!_cachelist.empty()) _cachelist.erase(((*itMap).second)->it); + + /* remove from cache */ + free((*itMap).second->info.data); + _totalSize -= (*itMap).second->size; + delete (*itMap).second; + _cache.erase(itMap); + + DBG_INFO(80, L"removed from cache: checksum = %08X %08X\n", (uint32)(checksum & 0xffffffff), (uint32)(checksum >> 32)); + + return 1; + } + + return 0; +} + +boolean +TxCache::is_cached(uint64 checksum) +{ + std::map::iterator itMap = _cache.find(checksum); + if (itMap != _cache.end()) return 1; + + return 0; +} + +void +TxCache::clear() +{ + if (!_cache.empty()) { + std::map::iterator itMap = _cache.begin(); + while (itMap != _cache.end()) { + free((*itMap).second->info.data); + delete (*itMap).second; + itMap++; + } + _cache.clear(); + } + + if (!_cachelist.empty()) _cachelist.clear(); + + _totalSize = 0; +}