Path: blob/master/libmupen64plus/mupen64plus-video-glide64mk2/src/GlideHQ/TxCache.cpp
2 views
/*1* Texture Filtering2* Version: 1.03*4* Copyright (C) 2007 Hiroshi Morii All Rights Reserved.5* Email koolsmoky(at)users.sourceforge.net6* Web http://www.3dfxzone.it/koolsmoky7*8* this is free software; you can redistribute it and/or modify9* it under the terms of the GNU General Public License as published by10* the Free Software Foundation; either version 2, or (at your option)11* any later version.12*13* this is distributed in the hope that it will be useful,14* but WITHOUT ANY WARRANTY; without even the implied warranty of15* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the16* GNU General Public License for more details.17*18* You should have received a copy of the GNU General Public License19* along with GNU Make; see the file COPYING. If not, write to20* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.21*/2223#ifdef __MSC__24#pragma warning(disable: 4786)25#endif2627#include <boost/filesystem.hpp>28#include <zlib.h>29#include "TxCache.h"30#include "TxDbg.h"31#include "../Glide64/m64p.h"32#include "../Glide64/Gfx_1.3.h"3334TxCache::~TxCache()35{36/* free memory, clean up, etc */37clear();3839delete _txUtil;40}4142TxCache::TxCache(int options, int cachesize, const wchar_t *datapath,43const wchar_t *cachepath, const wchar_t *ident,44dispInfoFuncExt callback)45{46_txUtil = new TxUtil();4748_options = options;49_cacheSize = cachesize;50_callback = callback;51_totalSize = 0;5253/* save path name */54if (datapath)55_datapath.assign(datapath);56if (cachepath)57_cachepath.assign(cachepath);5859/* save ROM name */60if (ident)61_ident.assign(ident);6263/* zlib memory buffers to (de)compress hires textures */64if (_options & (GZ_TEXCACHE|GZ_HIRESTEXCACHE)) {65_gzdest0 = TxMemBuf::getInstance()->get(0);66_gzdest1 = TxMemBuf::getInstance()->get(1);67_gzdestLen = (TxMemBuf::getInstance()->size_of(0) < TxMemBuf::getInstance()->size_of(1)) ?68TxMemBuf::getInstance()->size_of(0) : TxMemBuf::getInstance()->size_of(1);6970if (!_gzdest0 || !_gzdest1 || !_gzdestLen) {71_options &= ~(GZ_TEXCACHE|GZ_HIRESTEXCACHE);72_gzdest0 = NULL;73_gzdest1 = NULL;74_gzdestLen = 0;75}76}77}7879boolean80TxCache::add(uint64 checksum, GHQTexInfo *info, int dataSize)81{82/* NOTE: dataSize must be provided if info->data is zlib compressed. */8384if (!checksum || !info->data) return 0;8586uint8 *dest = info->data;87uint16 format = info->format;8889if (!dataSize) {90dataSize = _txUtil->sizeofTx(info->width, info->height, info->format);9192if (!dataSize) return 0;9394if (_options & (GZ_TEXCACHE|GZ_HIRESTEXCACHE)) {95/* zlib compress it. compression level:1 (best speed) */96uLongf destLen = _gzdestLen;97dest = (dest == _gzdest0) ? _gzdest1 : _gzdest0;98if (compress2(dest, &destLen, info->data, dataSize, 1) != Z_OK) {99dest = info->data;100DBG_INFO(80, L"Error: zlib compression failed!\n");101} else {102DBG_INFO(80, L"zlib compressed: %.02fkb->%.02fkb\n", (float)dataSize/1000, (float)destLen/1000);103dataSize = destLen;104format |= GR_TEXFMT_GZ;105}106}107}108109/* if cache size exceeds limit, remove old cache */110if (_cacheSize > 0) {111_totalSize += dataSize;112if ((_totalSize > _cacheSize) && !_cachelist.empty()) {113/* _cachelist is arranged so that frequently used textures are in the back */114std::list<uint64>::iterator itList = _cachelist.begin();115while (itList != _cachelist.end()) {116/* find it in _cache */117std::map<uint64, TXCACHE*>::iterator itMap = _cache.find(*itList);118if (itMap != _cache.end()) {119/* yep we have it. remove it. */120_totalSize -= (*itMap).second->size;121free((*itMap).second->info.data);122delete (*itMap).second;123_cache.erase(itMap);124}125itList++;126127/* check if memory cache has enough space */128if (_totalSize <= _cacheSize)129break;130}131/* remove from _cachelist */132_cachelist.erase(_cachelist.begin(), itList);133134DBG_INFO(80, L"+++++++++\n");135}136_totalSize -= dataSize;137}138139/* cache it */140uint8 *tmpdata = (uint8*)malloc(dataSize);141if (tmpdata) {142TXCACHE *txCache = new TXCACHE;143if (txCache) {144/* we can directly write as we filter, but for now we get away145* with doing memcpy after all the filtering is done.146*/147memcpy(tmpdata, dest, dataSize);148149/* copy it */150memcpy(&txCache->info, info, sizeof(GHQTexInfo));151txCache->info.data = tmpdata;152txCache->info.format = format;153txCache->size = dataSize;154155/* add to cache */156if (_cacheSize > 0) {157_cachelist.push_back(checksum);158txCache->it = --(_cachelist.end());159}160/* _cache[checksum] = txCache; */161_cache.insert(std::map<uint64, TXCACHE*>::value_type(checksum, txCache));162163#ifdef DEBUG164DBG_INFO(80, L"[%5d] added!! crc:%08X %08X %d x %d gfmt:%x total:%.02fmb\n",165_cache.size(), (uint32)(checksum >> 32), (uint32)(checksum & 0xffffffff),166info->width, info->height, info->format, (float)_totalSize/1000000);167168DBG_INFO(80, L"smalllodlog2:%d largelodlog2:%d aspectratiolog2:%d\n",169txCache->info.smallLodLog2, txCache->info.largeLodLog2, txCache->info.aspectRatioLog2);170171if (info->tiles) {172DBG_INFO(80, L"tiles:%d un-tiled size:%d x %d\n", info->tiles, info->untiled_width, info->untiled_height);173}174175if (_cacheSize > 0) {176DBG_INFO(80, L"cache max config:%.02fmb\n", (float)_cacheSize/1000000);177178if (_cache.size() != _cachelist.size()) {179DBG_INFO(80, L"Error: cache/cachelist mismatch! (%d/%d)\n", _cache.size(), _cachelist.size());180}181}182#endif183184/* total cache size */185_totalSize += dataSize;186187return 1;188}189free(tmpdata);190}191192return 0;193}194195boolean196TxCache::get(uint64 checksum, GHQTexInfo *info)197{198if (!checksum || _cache.empty()) return 0;199200/* find a match in cache */201std::map<uint64, TXCACHE*>::iterator itMap = _cache.find(checksum);202if (itMap != _cache.end()) {203/* yep, we've got it. */204memcpy(info, &(((*itMap).second)->info), sizeof(GHQTexInfo));205206/* push it to the back of the list */207if (_cacheSize > 0) {208_cachelist.erase(((*itMap).second)->it);209_cachelist.push_back(checksum);210((*itMap).second)->it = --(_cachelist.end());211}212213/* zlib decompress it */214if (info->format & GR_TEXFMT_GZ) {215uLongf destLen = _gzdestLen;216uint8 *dest = (_gzdest0 == info->data) ? _gzdest1 : _gzdest0;217if (uncompress(dest, &destLen, info->data, ((*itMap).second)->size) != Z_OK) {218DBG_INFO(80, L"Error: zlib decompression failed!\n");219return 0;220}221info->data = dest;222info->format &= ~GR_TEXFMT_GZ;223DBG_INFO(80, L"zlib decompressed: %.02fkb->%.02fkb\n", (float)(((*itMap).second)->size)/1000, (float)destLen/1000);224}225226return 1;227}228229return 0;230}231232boolean233TxCache::save(const wchar_t *path, const wchar_t *filename, int config)234{235if (!_cache.empty()) {236/* dump cache to disk */237char cbuf[MAX_PATH];238239boost::filesystem::wpath cachepath(path);240boost::filesystem::create_directory(cachepath);241242/* Ugly hack to enable fopen/gzopen in Win9x */243#ifdef BOOST_WINDOWS_API244wchar_t curpath[MAX_PATH];245GETCWD(MAX_PATH, curpath);246CHDIR(cachepath.wstring().c_str());247#else248char curpath[MAX_PATH];249wcstombs(cbuf, cachepath.wstring().c_str(), MAX_PATH);250if (GETCWD(MAX_PATH, curpath) == NULL)251ERRLOG("Error while retrieving working directory!");252if (CHDIR(cbuf) != 0)253ERRLOG("Error while changing current directory to '%s'!", cbuf);254#endif255256wcstombs(cbuf, filename, MAX_PATH);257258gzFile gzfp = gzopen(cbuf, "wb1");259DBG_INFO(80, L"gzfp:%x file:%ls\n", gzfp, filename);260if (gzfp) {261/* write header to determine config match */262gzwrite(gzfp, &config, 4);263264std::map<uint64, TXCACHE*>::iterator itMap = _cache.begin();265while (itMap != _cache.end()) {266uint8 *dest = (*itMap).second->info.data;267uint32 destLen = (*itMap).second->size;268uint16 format = (*itMap).second->info.format;269270/* to keep things simple, we save the texture data in a zlib uncompressed state. */271/* sigh... for those who cannot wait the extra few seconds. changed to keep272* texture data in a zlib compressed state. if the GZ_TEXCACHE or GZ_HIRESTEXCACHE273* option is toggled, the cache will need to be rebuilt.274*/275/*if (format & GR_TEXFMT_GZ) {276dest = _gzdest0;277destLen = _gzdestLen;278if (dest && destLen) {279if (uncompress(dest, &destLen, (*itMap).second->info.data, (*itMap).second->size) != Z_OK) {280dest = NULL;281destLen = 0;282}283format &= ~GR_TEXFMT_GZ;284}285}*/286287if (dest && destLen) {288/* texture checksum */289gzwrite(gzfp, &((*itMap).first), 8);290291/* other texture info */292gzwrite(gzfp, &((*itMap).second->info.width), 4);293gzwrite(gzfp, &((*itMap).second->info.height), 4);294gzwrite(gzfp, &format, 2);295296gzwrite(gzfp, &((*itMap).second->info.smallLodLog2), 4);297gzwrite(gzfp, &((*itMap).second->info.largeLodLog2), 4);298gzwrite(gzfp, &((*itMap).second->info.aspectRatioLog2), 4);299300gzwrite(gzfp, &((*itMap).second->info.tiles), 4);301gzwrite(gzfp, &((*itMap).second->info.untiled_width), 4);302gzwrite(gzfp, &((*itMap).second->info.untiled_height), 4);303304gzwrite(gzfp, &((*itMap).second->info.is_hires_tex), 1);305306gzwrite(gzfp, &destLen, 4);307gzwrite(gzfp, dest, destLen);308}309310itMap++;311312/* not ready yet */313/*if (_callback)314(*_callback)(L"Total textures saved to HDD: %d\n", std::distance(itMap, _cache.begin()));*/315}316gzclose(gzfp);317}318319if (CHDIR(curpath) != 0)320ERRLOG("Error while changing current directory back to original path of '%s'!", curpath);321}322323return _cache.empty();324}325326boolean327TxCache::load(const wchar_t *path, const wchar_t *filename, int config)328{329/* find it on disk */330char cbuf[MAX_PATH];331332boost::filesystem::wpath cachepath(path);333334#ifdef BOOST_WINDOWS_API335wchar_t curpath[MAX_PATH];336GETCWD(MAX_PATH, curpath);337CHDIR(cachepath.wstring().c_str());338#else339char curpath[MAX_PATH];340wcstombs(cbuf, cachepath.wstring().c_str(), MAX_PATH);341if (GETCWD(MAX_PATH, curpath) == NULL)342ERRLOG("Error while retrieving working directory!");343if (CHDIR(cbuf) != 0)344ERRLOG("Error while changing current directory to '%s'!", cbuf);345#endif346347wcstombs(cbuf, filename, MAX_PATH);348349gzFile gzfp = gzopen(cbuf, "rb");350DBG_INFO(80, L"gzfp:%x file:%ls\n", gzfp, filename);351if (gzfp) {352/* yep, we have it. load it into memory cache. */353int dataSize;354uint64 checksum;355GHQTexInfo tmpInfo;356int tmpconfig;357/* read header to determine config match */358gzread(gzfp, &tmpconfig, 4);359360if (tmpconfig == config) {361do {362memset(&tmpInfo, 0, sizeof(GHQTexInfo));363364gzread(gzfp, &checksum, 8);365366gzread(gzfp, &tmpInfo.width, 4);367gzread(gzfp, &tmpInfo.height, 4);368gzread(gzfp, &tmpInfo.format, 2);369370gzread(gzfp, &tmpInfo.smallLodLog2, 4);371gzread(gzfp, &tmpInfo.largeLodLog2, 4);372gzread(gzfp, &tmpInfo.aspectRatioLog2, 4);373374gzread(gzfp, &tmpInfo.tiles, 4);375gzread(gzfp, &tmpInfo.untiled_width, 4);376gzread(gzfp, &tmpInfo.untiled_height, 4);377378gzread(gzfp, &tmpInfo.is_hires_tex, 1);379380gzread(gzfp, &dataSize, 4);381382tmpInfo.data = (uint8*)malloc(dataSize);383if (tmpInfo.data) {384gzread(gzfp, tmpInfo.data, dataSize);385386/* add to memory cache */387add(checksum, &tmpInfo, (tmpInfo.format & GR_TEXFMT_GZ) ? dataSize : 0);388389free(tmpInfo.data);390} else {391gzseek(gzfp, dataSize, SEEK_CUR);392}393394/* skip in between to prevent the loop from being tied down to vsync */395if (_callback && (!(_cache.size() % 100) || gzeof(gzfp)))396(*_callback)(L"[%d] total mem:%.02fmb - %ls\n", _cache.size(), (float)_totalSize/1000000, filename);397398} while (!gzeof(gzfp));399gzclose(gzfp);400} else {401if ((tmpconfig & HIRESTEXTURES_MASK) != (config & HIRESTEXTURES_MASK)) {402const char *conf_str;403if ((tmpconfig & HIRESTEXTURES_MASK) == NO_HIRESTEXTURES)404conf_str = "0";405else if ((tmpconfig & HIRESTEXTURES_MASK) == RICE_HIRESTEXTURES)406conf_str = "1";407else408conf_str = "set to an unsupported format";409410WriteLog(M64MSG_WARNING, "Ignored texture cache due to incompatible setting: ghq_hirs must be %s", conf_str);411}412if ((tmpconfig & COMPRESS_HIRESTEX) != (config & COMPRESS_HIRESTEX))413WriteLog(M64MSG_WARNING, "Ignored texture cache due to incompatible setting: ghq_hirs_cmpr must be %s", (tmpconfig & COMPRESS_HIRESTEX) ? "True" : "False");414if ((tmpconfig & COMPRESSION_MASK) != (config & COMPRESSION_MASK) && (tmpconfig & COMPRESS_HIRESTEX)) {415const char *conf_str;416if ((tmpconfig & COMPRESSION_MASK) == FXT1_COMPRESSION)417conf_str = "1";418else if ((tmpconfig & COMPRESSION_MASK) == S3TC_COMPRESSION)419conf_str = "0";420else421conf_str = "set to an unsupported format";422423WriteLog(M64MSG_WARNING, "Ignored texture cache due to incompatible setting: ghq_cmpr must be %s", conf_str);424}425if ((tmpconfig & TILE_HIRESTEX) != (config & TILE_HIRESTEX))426WriteLog(M64MSG_WARNING, "Ignored texture cache due to incompatible setting: ghq_hirs_tile must be %s", (tmpconfig & TILE_HIRESTEX) ? "True" : "False");427if ((tmpconfig & FORCE16BPP_HIRESTEX) != (config & FORCE16BPP_HIRESTEX))428WriteLog(M64MSG_WARNING, "Ignored texture cache due to incompatible setting: ghq_hirs_f16bpp must be %s", (tmpconfig & FORCE16BPP_HIRESTEX) ? "True" : "False");429if ((tmpconfig & GZ_HIRESTEXCACHE) != (config & GZ_HIRESTEXCACHE))430WriteLog(M64MSG_WARNING, "ghq_hirs_gz must be %s", (tmpconfig & GZ_HIRESTEXCACHE) ? "True" : "False");431if ((tmpconfig & LET_TEXARTISTS_FLY) != (config & LET_TEXARTISTS_FLY))432WriteLog(M64MSG_WARNING, "Ignored texture cache due to incompatible setting: ghq_hirs_let_texartists_fly must be %s", (tmpconfig & LET_TEXARTISTS_FLY) ? "True" : "False");433434if ((tmpconfig & FILTER_MASK) != (config & FILTER_MASK)) {435const char *conf_str;436if ((tmpconfig & FILTER_MASK) == NO_FILTER)437conf_str = "0";438else if ((tmpconfig & FILTER_MASK) == SMOOTH_FILTER_1)439conf_str = "1";440else if ((tmpconfig & FILTER_MASK) == SMOOTH_FILTER_2)441conf_str = "2";442else if ((tmpconfig & FILTER_MASK) == SMOOTH_FILTER_3)443conf_str = "3";444else if ((tmpconfig & FILTER_MASK) == SMOOTH_FILTER_4)445conf_str = "4";446else if ((tmpconfig & FILTER_MASK) == SHARP_FILTER_1)447conf_str = "5";448else if ((tmpconfig & FILTER_MASK) == SHARP_FILTER_2)449conf_str = "6";450else451conf_str = "set to an unsupported format";452WriteLog(M64MSG_WARNING, "Ignored texture cache due to incompatible setting: ghq_fltr must be %s", conf_str);453}454455if ((tmpconfig & ENHANCEMENT_MASK) != (config & ENHANCEMENT_MASK)) {456const char *conf_str;457if ((tmpconfig & ENHANCEMENT_MASK) == NO_ENHANCEMENT)458conf_str = "0";459else if ((tmpconfig & ENHANCEMENT_MASK) == X2_ENHANCEMENT)460conf_str = "2";461else if ((tmpconfig & ENHANCEMENT_MASK) == X2SAI_ENHANCEMENT)462conf_str = "3";463else if ((tmpconfig & ENHANCEMENT_MASK) == HQ2X_ENHANCEMENT)464conf_str = "4";465else if ((tmpconfig & ENHANCEMENT_MASK) == HQ2XS_ENHANCEMENT)466conf_str = "5";467else if ((tmpconfig & ENHANCEMENT_MASK) == LQ2X_ENHANCEMENT)468conf_str = "6";469else if ((tmpconfig & ENHANCEMENT_MASK) == LQ2XS_ENHANCEMENT)470conf_str = "7";471else if ((tmpconfig & ENHANCEMENT_MASK) == HQ4X_ENHANCEMENT)472conf_str = "8";473else474conf_str = "set to an unsupported format";475WriteLog(M64MSG_WARNING, "Ignored texture cache due to incompatible setting: ghq_enht must be %s", conf_str);476}477478if ((tmpconfig & COMPRESS_TEX) != (config & COMPRESS_TEX))479WriteLog(M64MSG_WARNING, "Ignored texture cache due to incompatible setting: ghq_enht_cmpr must be %s", (tmpconfig & COMPRESS_TEX) ? "True" : "False");480if ((tmpconfig & FORCE16BPP_TEX) != (config & FORCE16BPP_TEX))481WriteLog(M64MSG_WARNING, "Ignored texture cache due to incompatible setting: ghq_enht_f16bpp must be %s", (tmpconfig & FORCE16BPP_TEX) ? "True" : "False");482if ((tmpconfig & GZ_TEXCACHE) != (config & GZ_TEXCACHE))483WriteLog(M64MSG_WARNING, "Ignored texture cache due to incompatible setting: ghq_enht_gz must be %s", (tmpconfig & GZ_TEXCACHE) ? "True" : "False");484}485}486487if (CHDIR(curpath) != 0)488ERRLOG("Error while changing current directory back to original path of '%s'!", curpath);489490return !_cache.empty();491}492493boolean494TxCache::del(uint64 checksum)495{496if (!checksum || _cache.empty()) return 0;497498std::map<uint64, TXCACHE*>::iterator itMap = _cache.find(checksum);499if (itMap != _cache.end()) {500501/* for texture cache (not hi-res cache) */502if (!_cachelist.empty()) _cachelist.erase(((*itMap).second)->it);503504/* remove from cache */505free((*itMap).second->info.data);506_totalSize -= (*itMap).second->size;507delete (*itMap).second;508_cache.erase(itMap);509510DBG_INFO(80, L"removed from cache: checksum = %08X %08X\n", (uint32)(checksum & 0xffffffff), (uint32)(checksum >> 32));511512return 1;513}514515return 0;516}517518boolean519TxCache::is_cached(uint64 checksum)520{521std::map<uint64, TXCACHE*>::iterator itMap = _cache.find(checksum);522if (itMap != _cache.end()) return 1;523524return 0;525}526527void528TxCache::clear()529{530if (!_cache.empty()) {531std::map<uint64, TXCACHE*>::iterator itMap = _cache.begin();532while (itMap != _cache.end()) {533free((*itMap).second->info.data);534delete (*itMap).second;535itMap++;536}537_cache.clear();538}539540if (!_cachelist.empty()) _cachelist.clear();541542_totalSize = 0;543}544545546