Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/libmupen64plus/mupen64plus-video-glide64mk2/src/GlideHQ/TxCache.cpp
2 views
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
35
TxCache::~TxCache()
36
{
37
/* free memory, clean up, etc */
38
clear();
39
40
delete _txUtil;
41
}
42
43
TxCache::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
80
boolean
81
TxCache::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
196
boolean
197
TxCache::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
233
boolean
234
TxCache::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
327
boolean
328
TxCache::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
494
boolean
495
TxCache::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
519
boolean
520
TxCache::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
528
void
529
TxCache::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
}
545
546