Glide Plugin GLES2 port from mupen64plus-ae, but with special FrameSkip code
[mupen64plus-pandora.git] / source / gles2glide64 / src / GlideHQ / TxCache.cpp
CommitLineData
98e75f2d 1/*
2 * Texture Filtering
3 * Version: 1.0
4 *
5 * Copyright (C) 2007 Hiroshi Morii All Rights Reserved.
6 * Email koolsmoky(at)users.sourceforge.net
7 * Web http://www.3dfxzone.it/koolsmoky
8 *
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)
12 * any later version.
13 *
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.
18 *
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.
22 */
23
24#ifdef __MSC__
25#pragma warning(disable: 4786)
26#endif
27
28#include <boost/filesystem.hpp>
29#include <zlib.h>
30#include "TxCache.h"
31#include "TxDbg.h"
32#include "../Glide64/m64p.h"
33#include "../Glide64/Gfx_1.3.h"
34
35TxCache::~TxCache()
36{
37 /* free memory, clean up, etc */
38 clear();
39
40 delete _txUtil;
41}
42
43TxCache::TxCache(int options, int cachesize, const wchar_t *datapath,
44 const wchar_t *cachepath, const wchar_t *ident,
45 dispInfoFuncExt callback)
46{
47 _txUtil = new TxUtil();
48
49 _options = options;
50 _cacheSize = cachesize;
51 _callback = callback;
52 _totalSize = 0;
53
54 /* save path name */
55 if (datapath)
56 _datapath.assign(datapath);
57 if (cachepath)
58 _cachepath.assign(cachepath);
59
60 /* save ROM name */
61 if (ident)
62 _ident.assign(ident);
63
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);
70
71 if (!_gzdest0 || !_gzdest1 || !_gzdestLen) {
72 _options &= ~(GZ_TEXCACHE|GZ_HIRESTEXCACHE);
73 _gzdest0 = NULL;
74 _gzdest1 = NULL;
75 _gzdestLen = 0;
76 }
77 }
78}
79
80boolean
81TxCache::add(uint64 checksum, GHQTexInfo *info, int dataSize)
82{
83 /* NOTE: dataSize must be provided if info->data is zlib compressed. */
84
85 if (!checksum || !info->data) return 0;
86
87 uint8 *dest = info->data;
88 uint16 format = info->format;
89
90 if (!dataSize) {
91 dataSize = _txUtil->sizeofTx(info->width, info->height, info->format);
92
93 if (!dataSize) return 0;
94
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) {
100 dest = info->data;
101 DBG_INFO(80, L"Error: zlib compression failed!\n");
102 } else {
103 DBG_INFO(80, L"zlib compressed: %.02fkb->%.02fkb\n", (float)dataSize/1000, (float)destLen/1000);
104 dataSize = destLen;
105 format |= GR_TEXFMT_GZ;
106 }
107 }
108 }
109
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;
124 _cache.erase(itMap);
125 }
126 itList++;
127
128 /* check if memory cache has enough space */
129 if (_totalSize <= _cacheSize)
130 break;
131 }
132 /* remove from _cachelist */
133 _cachelist.erase(_cachelist.begin(), itList);
134
135 DBG_INFO(80, L"+++++++++\n");
136 }
137 _totalSize -= dataSize;
138 }
139
140 /* cache it */
141 uint8 *tmpdata = (uint8*)malloc(dataSize);
142 if (tmpdata) {
143 TXCACHE *txCache = new TXCACHE;
144 if (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.
147 */
148 memcpy(tmpdata, dest, dataSize);
149
150 /* copy it */
151 memcpy(&txCache->info, info, sizeof(GHQTexInfo));
152 txCache->info.data = tmpdata;
153 txCache->info.format = format;
154 txCache->size = dataSize;
155
156 /* add to cache */
157 if (_cacheSize > 0) {
158 _cachelist.push_back(checksum);
159 txCache->it = --(_cachelist.end());
160 }
161 /* _cache[checksum] = txCache; */
162 _cache.insert(std::map<uint64, TXCACHE*>::value_type(checksum, txCache));
163
164#ifdef DEBUG
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);
168
169 DBG_INFO(80, L"smalllodlog2:%d largelodlog2:%d aspectratiolog2:%d\n",
170 txCache->info.smallLodLog2, txCache->info.largeLodLog2, txCache->info.aspectRatioLog2);
171
172 if (info->tiles) {
173 DBG_INFO(80, L"tiles:%d un-tiled size:%d x %d\n", info->tiles, info->untiled_width, info->untiled_height);
174 }
175
176 if (_cacheSize > 0) {
177 DBG_INFO(80, L"cache max config:%.02fmb\n", (float)_cacheSize/1000000);
178
179 if (_cache.size() != _cachelist.size()) {
180 DBG_INFO(80, L"Error: cache/cachelist mismatch! (%d/%d)\n", _cache.size(), _cachelist.size());
181 }
182 }
183#endif
184
185 /* total cache size */
186 _totalSize += dataSize;
187
188 return 1;
189 }
190 free(tmpdata);
191 }
192
193 return 0;
194}
195
196boolean
197TxCache::get(uint64 checksum, GHQTexInfo *info)
198{
199 if (!checksum || _cache.empty()) return 0;
200
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));
206
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());
212 }
213
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");
220 return 0;
221 }
222 info->data = dest;
223 info->format &= ~GR_TEXFMT_GZ;
224 DBG_INFO(80, L"zlib decompressed: %.02fkb->%.02fkb\n", (float)(((*itMap).second)->size)/1000, (float)destLen/1000);
225 }
226
227 return 1;
228 }
229
230 return 0;
231}
232
233boolean
234TxCache::save(const wchar_t *path, const wchar_t *filename, int config)
235{
236 if (!_cache.empty()) {
237 /* dump cache to disk */
238 char cbuf[MAX_PATH];
239
240 boost::filesystem::wpath cachepath(path);
241 boost::filesystem::create_directory(cachepath);
242
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());
248#else
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);
255#endif
256
257 wcstombs(cbuf, filename, MAX_PATH);
258
259 gzFile gzfp = gzopen(cbuf, "wb1");
260 DBG_INFO(80, L"gzfp:%x file:%ls\n", gzfp, filename);
261 if (gzfp) {
262 /* write header to determine config match */
263 gzwrite(gzfp, &config, 4);
264
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;
270
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.
275 */
276 /*if (format & GR_TEXFMT_GZ) {
277 dest = _gzdest0;
278 destLen = _gzdestLen;
279 if (dest && destLen) {
280 if (uncompress(dest, &destLen, (*itMap).second->info.data, (*itMap).second->size) != Z_OK) {
281 dest = NULL;
282 destLen = 0;
283 }
284 format &= ~GR_TEXFMT_GZ;
285 }
286 }*/
287
288 if (dest && destLen) {
289 /* texture checksum */
290 gzwrite(gzfp, &((*itMap).first), 8);
291
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);
296
297 gzwrite(gzfp, &((*itMap).second->info.smallLodLog2), 4);
298 gzwrite(gzfp, &((*itMap).second->info.largeLodLog2), 4);
299 gzwrite(gzfp, &((*itMap).second->info.aspectRatioLog2), 4);
300
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);
304
305 gzwrite(gzfp, &((*itMap).second->info.is_hires_tex), 1);
306
307 gzwrite(gzfp, &destLen, 4);
308 gzwrite(gzfp, dest, destLen);
309 }
310
311 itMap++;
312
313 /* not ready yet */
314 /*if (_callback)
315 (*_callback)(L"Total textures saved to HDD: %d\n", std::distance(itMap, _cache.begin()));*/
316 }
317 gzclose(gzfp);
318 }
319
320 if (CHDIR(curpath) != 0)
321 ERRLOG("Error while changing current directory back to original path of '%s'!", curpath);
322 }
323
324 return _cache.empty();
325}
326
327boolean
328TxCache::load(const wchar_t *path, const wchar_t *filename, int config)
329{
330 /* find it on disk */
331 char cbuf[MAX_PATH];
332
333 boost::filesystem::wpath cachepath(path);
334
335#ifdef BOOST_WINDOWS_API
336 wchar_t curpath[MAX_PATH];
337 GETCWD(MAX_PATH, curpath);
338 CHDIR(cachepath.wstring().c_str());
339#else
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);
346#endif
347
348 wcstombs(cbuf, filename, MAX_PATH);
349
350 gzFile gzfp = gzopen(cbuf, "rb");
351 DBG_INFO(80, L"gzfp:%x file:%ls\n", gzfp, filename);
352 if (gzfp) {
353 /* yep, we have it. load it into memory cache. */
354 int dataSize;
355 uint64 checksum;
356 GHQTexInfo tmpInfo;
357 int tmpconfig;
358 /* read header to determine config match */
359 gzread(gzfp, &tmpconfig, 4);
360
361 if (tmpconfig == config) {
362 do {
363 memset(&tmpInfo, 0, sizeof(GHQTexInfo));
364
365 gzread(gzfp, &checksum, 8);
366
367 gzread(gzfp, &tmpInfo.width, 4);
368 gzread(gzfp, &tmpInfo.height, 4);
369 gzread(gzfp, &tmpInfo.format, 2);
370
371 gzread(gzfp, &tmpInfo.smallLodLog2, 4);
372 gzread(gzfp, &tmpInfo.largeLodLog2, 4);
373 gzread(gzfp, &tmpInfo.aspectRatioLog2, 4);
374
375 gzread(gzfp, &tmpInfo.tiles, 4);
376 gzread(gzfp, &tmpInfo.untiled_width, 4);
377 gzread(gzfp, &tmpInfo.untiled_height, 4);
378
379 gzread(gzfp, &tmpInfo.is_hires_tex, 1);
380
381 gzread(gzfp, &dataSize, 4);
382
383 tmpInfo.data = (uint8*)malloc(dataSize);
384 if (tmpInfo.data) {
385 gzread(gzfp, tmpInfo.data, dataSize);
386
387 /* add to memory cache */
388 add(checksum, &tmpInfo, (tmpInfo.format & GR_TEXFMT_GZ) ? dataSize : 0);
389
390 free(tmpInfo.data);
391 } else {
392 gzseek(gzfp, dataSize, SEEK_CUR);
393 }
394
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);
398
399 } while (!gzeof(gzfp));
400 gzclose(gzfp);
401 } else {
402 if ((tmpconfig & HIRESTEXTURES_MASK) != (config & HIRESTEXTURES_MASK)) {
403 const char *conf_str;
404 if ((tmpconfig & HIRESTEXTURES_MASK) == NO_HIRESTEXTURES)
405 conf_str = "0";
406 else if ((tmpconfig & HIRESTEXTURES_MASK) == RICE_HIRESTEXTURES)
407 conf_str = "1";
408 else
409 conf_str = "set to an unsupported format";
410
411 WriteLog(M64MSG_WARNING, "Ignored texture cache due to incompatible setting: ghq_hirs must be %s", conf_str);
412 }
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)
418 conf_str = "1";
419 else if ((tmpconfig & COMPRESSION_MASK) == S3TC_COMPRESSION)
420 conf_str = "0";
421 else
422 conf_str = "set to an unsupported format";
423
424 WriteLog(M64MSG_WARNING, "Ignored texture cache due to incompatible setting: ghq_cmpr must be %s", conf_str);
425 }
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");
434
435 if ((tmpconfig & FILTER_MASK) != (config & FILTER_MASK)) {
436 const char *conf_str;
437 if ((tmpconfig & FILTER_MASK) == NO_FILTER)
438 conf_str = "0";
439 else if ((tmpconfig & FILTER_MASK) == SMOOTH_FILTER_1)
440 conf_str = "1";
441 else if ((tmpconfig & FILTER_MASK) == SMOOTH_FILTER_2)
442 conf_str = "2";
443 else if ((tmpconfig & FILTER_MASK) == SMOOTH_FILTER_3)
444 conf_str = "3";
445 else if ((tmpconfig & FILTER_MASK) == SMOOTH_FILTER_4)
446 conf_str = "4";
447 else if ((tmpconfig & FILTER_MASK) == SHARP_FILTER_1)
448 conf_str = "5";
449 else if ((tmpconfig & FILTER_MASK) == SHARP_FILTER_2)
450 conf_str = "6";
451 else
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);
454 }
455
456 if ((tmpconfig & ENHANCEMENT_MASK) != (config & ENHANCEMENT_MASK)) {
457 const char *conf_str;
458 if ((tmpconfig & ENHANCEMENT_MASK) == NO_ENHANCEMENT)
459 conf_str = "0";
460 else if ((tmpconfig & ENHANCEMENT_MASK) == X2_ENHANCEMENT)
461 conf_str = "2";
462 else if ((tmpconfig & ENHANCEMENT_MASK) == X2SAI_ENHANCEMENT)
463 conf_str = "3";
464 else if ((tmpconfig & ENHANCEMENT_MASK) == HQ2X_ENHANCEMENT)
465 conf_str = "4";
466 else if ((tmpconfig & ENHANCEMENT_MASK) == HQ2XS_ENHANCEMENT)
467 conf_str = "5";
468 else if ((tmpconfig & ENHANCEMENT_MASK) == LQ2X_ENHANCEMENT)
469 conf_str = "6";
470 else if ((tmpconfig & ENHANCEMENT_MASK) == LQ2XS_ENHANCEMENT)
471 conf_str = "7";
472 else if ((tmpconfig & ENHANCEMENT_MASK) == HQ4X_ENHANCEMENT)
473 conf_str = "8";
474 else
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);
477 }
478
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");
485 }
486 }
487
488 if (CHDIR(curpath) != 0)
489 ERRLOG("Error while changing current directory back to original path of '%s'!", curpath);
490
491 return !_cache.empty();
492}
493
494boolean
495TxCache::del(uint64 checksum)
496{
497 if (!checksum || _cache.empty()) return 0;
498
499 std::map<uint64, TXCACHE*>::iterator itMap = _cache.find(checksum);
500 if (itMap != _cache.end()) {
501
502 /* for texture cache (not hi-res cache) */
503 if (!_cachelist.empty()) _cachelist.erase(((*itMap).second)->it);
504
505 /* remove from cache */
506 free((*itMap).second->info.data);
507 _totalSize -= (*itMap).second->size;
508 delete (*itMap).second;
509 _cache.erase(itMap);
510
511 DBG_INFO(80, L"removed from cache: checksum = %08X %08X\n", (uint32)(checksum & 0xffffffff), (uint32)(checksum >> 32));
512
513 return 1;
514 }
515
516 return 0;
517}
518
519boolean
520TxCache::is_cached(uint64 checksum)
521{
522 std::map<uint64, TXCACHE*>::iterator itMap = _cache.find(checksum);
523 if (itMap != _cache.end()) return 1;
524
525 return 0;
526}
527
528void
529TxCache::clear()
530{
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;
536 itMap++;
537 }
538 _cache.clear();
539 }
540
541 if (!_cachelist.empty()) _cachelist.clear();
542
543 _totalSize = 0;
544}