Subversion Repositories eduke32

Rev

Rev 5055 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
3904 terminx 1
#ifdef USE_OPENGL
2
 
4317 hendricks2 3
#include "baselayer.h"
3758 terminx 4
#include "build.h"
4316 hendricks2 5
#include "lz4.h"
3758 terminx 6
#include "hightile.h"
7
#include "polymost.h"
8
#include "texcache.h"
9
#include "dxtfilter.h"
10
#include "scriptfile.h"
4387 terminx 11
#include "xxhash.h"
4639 terminx 12
#include "kplib.h"
3758 terminx 13
 
4997 terminx 14
#ifdef EDUKE32_GLES
15
#include "jwzgles.h"
16
#endif
17
 
3758 terminx 18
#define CLEAR_GL_ERRORS() while(bglGetError() != GL_NO_ERROR) { }
19
#define TEXCACHE_FREEBUFS() { Bfree(pic), Bfree(packbuf), Bfree(midbuf); }
20
 
3781 terminx 21
globaltexcache texcache;
3758 terminx 22
 
23
char TEXCACHEFILE[BMAX_PATH] = "textures";
24
 
3781 terminx 25
static const char *texcache_errorstr[TEXCACHEERRORS] = {
26
    "no error",
3758 terminx 27
    "out of memory!",
28
    "read too few bytes from cache file",
29
    "dedxtfilter failed",
30
    "bglCompressedTexImage2DARB failed",
31
    "bglGetTexLevelParameteriv failed",
32
};
33
 
4605 terminx 34
static pthtyp *texcache_tryart(int32_t dapicnum, int32_t dapalnum, int32_t dashade, int32_t dameth)
35
{
4661 terminx 36
    const int32_t j = dapicnum&(GLTEXCACHEADSIZ-1);
4605 terminx 37
    pthtyp *pth;
38
 
5055 hendricks2 39
    if ((hictinting[dapalnum].f & HICTINT_USEONART) && !(hictinting[dapalnum].f & HICTINT_APPLYOVERPALSWAP))
40
        dapalnum = 0;
41
 
4605 terminx 42
    // load from art
43
    for (pth=texcache.list[j]; pth; pth=pth->next)
44
        if (pth->picnum == dapicnum && pth->palnum == dapalnum && pth->shade == dashade &&
45
                (pth->flags & (PTH_CLAMPED+PTH_HIGHTILE)) == TO_PTH_CLAMPED(dameth) &&
4623 terminx 46
                polymost_want_npotytex(dameth, tilesiz[dapicnum].y) == !!(pth->flags&PTH_NPOTWALL)
4605 terminx 47
           )
48
        {
49
            if (pth->flags & PTH_INVALIDATED)
50
            {
51
                pth->flags &= ~PTH_INVALIDATED;
52
                gloadtile_art(dapicnum, dapalnum, dashade, dameth, pth, 0);
53
            }
54
 
55
            return(pth);
56
        }
57
 
58
    pth = (pthtyp *)Xcalloc(1,sizeof(pthtyp));
59
 
60
    gloadtile_art(dapicnum,dapalnum,dashade,dameth,pth,1);
61
 
62
    pth->next = texcache.list[j];
63
    texcache.list[j] = pth;
64
 
65
    return(pth);
66
}
67
 
68
pthtyp *texcache_fetchmulti(pthtyp *pth, hicreplctyp *si, int32_t dapicnum, int32_t dameth)
69
{
4661 terminx 70
    const int32_t j = dapicnum&(GLTEXCACHEADSIZ-1);
4605 terminx 71
    int32_t i;
72
 
4661 terminx 73
    for (i = 0; i <= (GLTEXCACHEADSIZ - 1); i++)
4605 terminx 74
    {
75
        const pthtyp *pth2;
76
 
77
        for (pth2=texcache.list[i]; pth2; pth2=pth2->next)
78
        {
4639 terminx 79
            if (pth2->hicr && pth2->hicr->filename && filnamcmp(pth2->hicr->filename, si->filename) == 0)
4605 terminx 80
            {
81
                Bmemcpy(pth, pth2, sizeof(pthtyp));
82
                pth->picnum = dapicnum;
83
                pth->flags = TO_PTH_CLAMPED(dameth) + PTH_HIGHTILE + (drawingskybox>0)*PTH_SKYBOX;
84
                if (pth2->flags & PTH_HASALPHA)
85
                    pth->flags |= PTH_HASALPHA;
86
                pth->hicr = si;
87
 
88
                pth->next = texcache.list[j];
89
                texcache.list[j] = pth;
90
 
91
                return pth;
92
            }
93
        }
94
    }
95
 
96
    return NULL;
97
}
98
 
4406 helixhorne 99
// <dashade>: ignored if not in Polymost+r_usetileshades
3761 terminx 100
pthtyp *texcache_fetch(int32_t dapicnum, int32_t dapalnum, int32_t dashade, int32_t dameth)
3758 terminx 101
{
4605 terminx 102
    int32_t tilestat;
4489 helixhorne 103
    pthtyp *pth;
3758 terminx 104
 
4752 terminx 105
    const int32_t j = dapicnum & (GLTEXCACHEADSIZ - 1);
4605 terminx 106
    hicreplctyp *si = usehightile ? hicfindsubst(dapicnum, dapalnum) : NULL;
4489 helixhorne 107
 
4605 terminx 108
    if (drawingskybox && usehightile)
109
        if ((si = hicfindskybox(dapicnum, dapalnum)) == NULL)
110
            return NULL;
111
 
5056 hendricks2 112
    if (!r_usetileshades || (globalflags & GLOBAL_NO_GL_TILESHADES) || getrendermode() != REND_POLYMOST)
4406 helixhorne 113
        dashade = 0;
3758 terminx 114
 
115
    if (!si)
116
    {
4752 terminx 117
        return (dapalnum >= (MAXPALOOKUPS - RESERVEDPALS) || hicprecaching) ?
118
                NULL : texcache_tryart(dapicnum, dapalnum, dashade, dameth);
3758 terminx 119
    }
120
 
121
    /* if palette > 0 && replacement found
122
     *    no effects are applied to the texture
123
     * else if palette > 0 && no replacement found
124
     *    effects are applied to the palette 0 texture if it exists
125
     */
126
 
127
    // load a replacement
4752 terminx 128
    for (pth = texcache.list[j]; pth; pth = pth->next)
3758 terminx 129
    {
130
        if (pth->picnum == dapicnum && pth->palnum == si->palnum &&
4752 terminx 131
            (si->palnum > 0 ? 1 : (pth->effects == hictinting[dapalnum].f)) &&
132
            (pth->flags & (PTH_CLAMPED + PTH_HIGHTILE + PTH_SKYBOX)) ==
133
            (TO_PTH_CLAMPED(dameth) + PTH_HIGHTILE + (drawingskybox > 0) * PTH_SKYBOX) &&
134
            (drawingskybox > 0 ? (pth->skyface == drawingskybox) : 1))
3758 terminx 135
        {
4486 helixhorne 136
            if (pth->flags & PTH_INVALIDATED)
3758 terminx 137
            {
4486 helixhorne 138
                pth->flags &= ~PTH_INVALIDATED;
139
 
4605 terminx 140
                tilestat = gloadtile_hi(dapicnum, dapalnum, drawingskybox, si, dameth, pth, 0,
4752 terminx 141
                                        (si->palnum > 0) ? 0 : hictinting[dapalnum].f);  // reload tile
4605 terminx 142
 
4752 terminx 143
                if (!tilestat)
144
                    continue;
145
 
146
                if (tilestat == -2)  // bad filename
147
                    hicclearsubst(dapicnum, dapalnum);
148
                return (drawingskybox || hicprecaching) ? NULL : texcache_tryart(dapicnum, dapalnum, dashade, dameth);
3758 terminx 149
            }
150
 
4752 terminx 151
            return (pth);
3758 terminx 152
        }
153
    }
154
 
4752 terminx 155
    pth = (pthtyp *)Xcalloc(1, sizeof(pthtyp));
3758 terminx 156
 
157
    // possibly fetch an already loaded multitexture :_)
4639 terminx 158
    if (dapalnum >= (MAXPALOOKUPS - RESERVEDPALS) && texcache_fetchmulti(pth, si, dapicnum, dameth))
4605 terminx 159
        return pth;
4489 helixhorne 160
 
4752 terminx 161
    tilestat =
162
    gloadtile_hi(dapicnum, dapalnum, drawingskybox, si, dameth, pth, 1, (si->palnum > 0) ? 0 : hictinting[dapalnum].f);
4489 helixhorne 163
 
4752 terminx 164
    if (!tilestat)
3758 terminx 165
    {
4752 terminx 166
        pth->next = texcache.list[j];
167
        pth->palnum = si->palnum;
168
        texcache.list[j] = pth;
169
        return pth;
3758 terminx 170
    }
171
 
4752 terminx 172
    if (tilestat == -2)  // bad filename
173
        hicclearsubst(dapicnum, dapalnum);
3758 terminx 174
 
4752 terminx 175
    Bfree(pth);
176
 
177
    return (drawingskybox || hicprecaching) ? NULL : texcache_tryart(dapicnum, dapalnum, dashade, dameth);
3758 terminx 178
}
179
 
180
static void texcache_closefiles(void)
181
{
3781 terminx 182
    if (texcache.filehandle != -1)
3758 terminx 183
    {
3781 terminx 184
        Bclose(texcache.filehandle);
185
        texcache.filehandle = -1;
3758 terminx 186
    }
4661 terminx 187
    MAYBE_FCLOSE_AND_NULL(texcache.index);
3758 terminx 188
}
189
 
190
void texcache_freeptrs(void)
191
{
4752 terminx 192
    texcache.iptrcnt = 0;
3758 terminx 193
 
4752 terminx 194
    if (!texcache.iptrs)
195
        return;
196
 
197
    for (int i = 0; i < texcache.numentries; i++)
4666 terminx 198
        if (texcache.iptrs[i])
3758 terminx 199
        {
4752 terminx 200
            for (int ii = texcache.numentries - 1; ii >= 0; ii--)
4666 terminx 201
                if (i != ii && texcache.iptrs[ii] == texcache.iptrs[i])
3758 terminx 202
                {
203
                    /*OSD_Printf("removing duplicate cacheptr %d\n",ii);*/
4666 terminx 204
                    texcache.iptrs[ii] = NULL;
3758 terminx 205
                }
206
 
4752 terminx 207
            Bfree(texcache.iptrs[i]);
208
            texcache.iptrs[i] = NULL;
3758 terminx 209
        }
4666 terminx 210
 
211
    DO_FREE_AND_NULL(texcache.iptrs);
3758 terminx 212
}
213
 
4661 terminx 214
static inline void texcache_clearmemcache(void)
3758 terminx 215
{
4661 terminx 216
    DO_FREE_AND_NULL(texcache.memcache.ptr);
3781 terminx 217
    texcache.memcache.size = -1;
3758 terminx 218
}
219
 
3763 terminx 220
void texcache_syncmemcache(void)
3758 terminx 221
{
4669 terminx 222
    int32_t len = Bfilelength(texcache.filehandle);
3758 terminx 223
 
4666 terminx 224
    if (!texcache.memcache.ptr || texcache.filehandle == -1 || len <= (int32_t)texcache.memcache.size)
3763 terminx 225
        return;
3758 terminx 226
 
3781 terminx 227
    texcache.memcache.ptr = (uint8_t *)Brealloc(texcache.memcache.ptr, len);
3763 terminx 228
 
3781 terminx 229
    if (!texcache.memcache.ptr)
3763 terminx 230
    {
231
        texcache_clearmemcache();
232
        initprintf("Failed syncing memcache to texcache, disabling memcache.\n");
3781 terminx 233
        texcache.memcache.noalloc = 1;
3763 terminx 234
    }
235
    else
236
    {
237
        initprintf("Syncing memcache to texcache\n");
3781 terminx 238
        Blseek(texcache.filehandle, texcache.memcache.size, BSEEK_SET);
239
        if (Bread(texcache.filehandle, texcache.memcache.ptr + texcache.memcache.size, len - texcache.memcache.size) != (bssize_t)(len-texcache.memcache.size))
3758 terminx 240
        {
3763 terminx 241
            initprintf("polymost_cachesync: Failed reading texcache into memcache!\n");
242
            texcache_clearmemcache();
3781 terminx 243
            texcache.memcache.noalloc = 1;
3758 terminx 244
        }
245
        else
246
        {
3781 terminx 247
            texcache.memcache.size = len;
3758 terminx 248
        }
249
    }
250
}
251
 
252
void texcache_init(void)
253
{
3781 terminx 254
    if (!texcache.index)
255
        texcache.filehandle = -1;
256
 
3758 terminx 257
    texcache_closefiles();
3763 terminx 258
    texcache_clearmemcache();
3758 terminx 259
    texcache_freeptrs();
260
 
4491 helixhorne 261
    texcache.currentindex = texcache.firstindex = (texcacheindex *)Xcalloc(1, sizeof(texcacheindex));
3781 terminx 262
    texcache.numentries = 0;
3758 terminx 263
 
264
    //    Bmemset(&firstcacheindex, 0, sizeof(texcacheindex));
265
    //    Bmemset(&cacheptrs[0], 0, sizeof(cacheptrs));
266
 
3781 terminx 267
    texcache.hashes.size = TEXCACHEHASHSIZE;
268
    hash_init(&texcache.hashes);
3758 terminx 269
}
270
 
3781 terminx 271
static void texcache_deletefiles(void)
3758 terminx 272
{
273
    Bstrcpy(ptempbuf, TEXCACHEFILE);
274
    unlink(ptempbuf);
275
    Bstrcat(ptempbuf, ".cache");
276
    unlink(ptempbuf);
3761 terminx 277
}
3758 terminx 278
 
3761 terminx 279
static int32_t texcache_enabled(void)
280
{
281
    if (!glusetexcompr || !glusetexcache) return 0;
282
 
283
    if (!glinfo.texcompr || !bglCompressedTexImage2DARB || !bglGetCompressedTexImageARB)
284
    {
285
        // lacking the necessary extensions to do this
286
        OSD_Printf("Warning: the GL driver lacks necessary functions to use caching\n");
287
        glusetexcache = 0;
288
        return 0;
289
    }
290
 
3781 terminx 291
    if (!texcache.index || texcache.filehandle < 0)
3761 terminx 292
    {
293
        OSD_Printf("Warning: no active cache!\n");
294
        return 0;
295
    }
296
 
297
    return 1;
298
}
299
 
300
void texcache_openfiles(void)
301
{
302
    Bstrcpy(ptempbuf,TEXCACHEFILE);
303
    Bstrcat(ptempbuf,".cache");
3781 terminx 304
    texcache.index = Bfopen(ptempbuf, "at+");
305
    texcache.filehandle = Bopen(TEXCACHEFILE, BO_BINARY|BO_CREAT|BO_APPEND|BO_RDWR, BS_IREAD|BS_IWRITE);
3758 terminx 306
 
3781 terminx 307
    if (!texcache.index || texcache.filehandle < 0)
3758 terminx 308
    {
309
        initprintf("Unable to open cache file \"%s\" or \"%s\": %s\n", TEXCACHEFILE, ptempbuf, strerror(errno));
310
        texcache_closefiles();
311
        glusetexcache = 0;
312
        return;
313
    }
314
 
3781 terminx 315
    Bfseek(texcache.index, 0, BSEEK_END);
316
    if (!Bftell(texcache.index))
3761 terminx 317
    {
3781 terminx 318
        Brewind(texcache.index);
319
        Bfprintf(texcache.index,"// automatically generated by EDuke32, DO NOT MODIFY!\n");
3761 terminx 320
    }
3781 terminx 321
    else Brewind(texcache.index);
3761 terminx 322
 
323
    initprintf("Opened \"%s\" as cache file\n", TEXCACHEFILE);
3758 terminx 324
}
325
 
3761 terminx 326
 
327
void texcache_checkgarbage(void)
328
{
329
    int32_t i = 0;
330
 
331
    if (!texcache_enabled())
332
        return;
333
 
3781 terminx 334
    texcache.currentindex = texcache.firstindex;
335
    while (texcache.currentindex->next)
3761 terminx 336
    {
3781 terminx 337
        i += texcache.currentindex->len;
338
        texcache.currentindex = texcache.currentindex->next;
3761 terminx 339
    }
340
 
3781 terminx 341
    i = Blseek(texcache.filehandle, 0, BSEEK_END)-i;
3761 terminx 342
 
343
    if (i)
344
        initprintf("Cache contains %d bytes of garbage data\n",i);
345
}
346
 
347
void texcache_invalidate(void)
348
{
349
#ifdef DEBUGGINGAIDS
350
    OSD_Printf("texcache_invalidate()\n");
351
#endif
352
    r_downsizevar = r_downsize; // update the cvar representation when the menu changes r_downsize
353
 
354
    polymost_glreset();
355
 
356
    texcache_init();
3781 terminx 357
    texcache_deletefiles();
3761 terminx 358
    texcache_openfiles();
359
}
360
 
3758 terminx 361
int32_t texcache_loadoffsets(void)
362
{
363
    int32_t foffset, fsize, i;
364
    char *fname;
365
 
366
    scriptfile *script;
367
 
368
    Bstrcpy(ptempbuf,TEXCACHEFILE);
369
    Bstrcat(ptempbuf,".cache");
370
    script = scriptfile_fromfile(ptempbuf);
371
 
372
    if (!script) return -1;
373
 
374
    while (!scriptfile_eof(script))
375
    {
376
        if (scriptfile_getstring(script, &fname)) break;        // hashed filename
377
        if (scriptfile_getnumber(script, &foffset)) break;      // offset in cache
378
        if (scriptfile_getnumber(script, &fsize)) break;        // size
379
 
3781 terminx 380
        i = hash_find(&texcache.hashes,fname);
3758 terminx 381
        if (i > -1)
382
        {
383
            // update an existing entry
4666 terminx 384
            texcacheindex *t = texcache.iptrs[i];
3758 terminx 385
            t->offset = foffset;
386
            t->len = fsize;
387
            /*initprintf("%s %d got a match for %s offset %d\n",__FILE__, __LINE__, fname,foffset);*/
388
        }
389
        else
390
        {
3781 terminx 391
            Bstrncpyz(texcache.currentindex->name, fname, BMAX_PATH);
392
            texcache.currentindex->offset = foffset;
393
            texcache.currentindex->len = fsize;
4491 helixhorne 394
            texcache.currentindex->next = (texcacheindex *)Xcalloc(1, sizeof(texcacheindex));
3781 terminx 395
            hash_add(&texcache.hashes, fname, texcache.numentries, 1);
4666 terminx 396
            if (++texcache.numentries > texcache.iptrcnt)
397
            {
398
                texcache.iptrcnt += 512;
399
                texcache.iptrs = (texcacheindex **) Xrealloc(texcache.iptrs, sizeof(intptr_t) * texcache.iptrcnt);
400
            }
401
            texcache.iptrs[texcache.numentries-1] = texcache.currentindex;
3781 terminx 402
            texcache.currentindex = texcache.currentindex->next;
3758 terminx 403
        }
404
    }
405
 
406
    scriptfile_close(script);
407
    return 0;
408
}
409
 
410
// Read from on-disk texcache or its in-memory cache.
411
int32_t texcache_readdata(void *dest, int32_t len)
412
{
3781 terminx 413
    const int32_t ocachepos = texcache.filepos;
3758 terminx 414
 
3781 terminx 415
    texcache.filepos += len;
3758 terminx 416
 
4669 terminx 417
    if (texcache.memcache.ptr && texcache.memcache.size >= ocachepos+len)
3758 terminx 418
    {
419
        //        initprintf("using memcache!\n");
3781 terminx 420
        Bmemcpy(dest, texcache.memcache.ptr+ocachepos, len);
421
        return 0;
3758 terminx 422
    }
423
 
3781 terminx 424
    if (Blseek(texcache.filehandle, ocachepos, BSEEK_SET) != ocachepos)
425
        return 1;
3758 terminx 426
 
3781 terminx 427
    if (Bread(texcache.filehandle, dest, len) < len)
428
        return 1;
429
 
3758 terminx 430
    return 0;
431
}
432
 
433
static const char * texcache_calcid(char *cachefn, const char *fn, const int32_t len, const int32_t dameth, const char effect)
434
{
4096 helixhorne 435
    // Assert that BMAX_PATH is a multiple of 4 so that struct texcacheid_t
436
    // gets no padding inserted by the compiler.
437
    EDUKE32_STATIC_ASSERT((BMAX_PATH & 3) == 0);
438
 
3781 terminx 439
    struct texcacheid_t {
440
        int32_t len, method;
4096 helixhorne 441
        char effect, name[BMAX_PATH+3];  // +3: pad to a multiple of 4
3781 terminx 442
    } id = { len, dameth, effect, "" };
3758 terminx 443
 
444
    Bstrcpy(id.name, fn);
445
 
446
    while (Bstrlen(id.name) < BMAX_PATH - Bstrlen(fn))
447
        Bstrcat(id.name, fn);
448
 
4096 helixhorne 449
    Bsprintf(cachefn, "%08x%08x%08x",
4387 terminx 450
        XXH32((uint8_t *)fn, Bstrlen(fn), TEXCACHEMAGIC[3]),
451
        XXH32((uint8_t *)id.name, Bstrlen(id.name), TEXCACHEMAGIC[3]),
452
        XXH32((uint8_t *)&id, sizeof(struct texcacheid_t), TEXCACHEMAGIC[3]));
3758 terminx 453
 
454
    return cachefn;
455
}
456
 
457
#define READTEXHEADER_FAILURE(x) { err = x; goto failure; }
458
 
459
// returns 1 on success
460
int32_t texcache_readtexheader(const char *fn, int32_t len, int32_t dameth, char effect,
461
                         texcacheheader *head, int32_t modelp)
462
{
463
    int32_t i, err = 0;
464
    char cachefn[BMAX_PATH];
465
 
4488 helixhorne 466
    if (!texcache_enabled())
467
        return 0;
3758 terminx 468
 
3781 terminx 469
    i = hash_find(&texcache.hashes, texcache_calcid(cachefn, fn, len, dameth, effect));
3758 terminx 470
 
4666 terminx 471
    if (i < 0 || !texcache.iptrs[i])
3758 terminx 472
        return 0;  // didn't find it
473
 
4666 terminx 474
    texcache.filepos = texcache.iptrs[i]->offset;
4488 helixhorne 475
//    initprintf("%s %d got a match for %s offset %d\n",__FILE__, __LINE__, cachefn,offset);
3758 terminx 476
 
4488 helixhorne 477
    if (texcache_readdata(head, sizeof(texcacheheader)))
478
        READTEXHEADER_FAILURE(0);
3758 terminx 479
 
4488 helixhorne 480
    if (Bmemcmp(head->magic, TEXCACHEMAGIC, 4))
481
        READTEXHEADER_FAILURE(1);
3758 terminx 482
 
483
    // native (little-endian) -> internal
484
    head->xdim = B_LITTLE32(head->xdim);
485
    head->ydim = B_LITTLE32(head->ydim);
486
    head->flags = B_LITTLE32(head->flags);
487
    head->quality = B_LITTLE32(head->quality);
488
 
4488 helixhorne 489
    if (modelp && head->quality != r_downsize)
490
        READTEXHEADER_FAILURE(2);
491
    if ((head->flags & CACHEAD_COMPRESSED) && glusetexcache != 2)
492
        READTEXHEADER_FAILURE(3);
493
    if (!(head->flags & CACHEAD_COMPRESSED) && glusetexcache == 2)
494
        READTEXHEADER_FAILURE(4);
3758 terminx 495
 
496
    // handle nocompress
4488 helixhorne 497
    if (!modelp && !(head->flags & CACHEAD_NOCOMPRESS) && head->quality != r_downsize)
3758 terminx 498
        return 0;
499
 
4488 helixhorne 500
    if (gltexmaxsize && (head->xdim > (1<<gltexmaxsize) || head->ydim > (1<<gltexmaxsize)))
501
        READTEXHEADER_FAILURE(5);
502
    if (!glinfo.texnpot && (head->flags & CACHEAD_NONPOW2))
503
        READTEXHEADER_FAILURE(6);
3758 terminx 504
 
505
    return 1;
506
 
507
failure:
508
    {
509
        static const char *error_msgs[] = {
510
            "failed reading texture cache header",  // 0
511
            "header magic string doesn't match",  // 1
512
            "r_downsize doesn't match",  // 2  (skins only)
513
            "compression doesn't match: cache contains compressed tex",  // 3
514
            "compression doesn't match: cache contains uncompressed tex",  // 4
515
            "texture in cache exceeds maximum supported size",  // 5
516
            "texture in cache has non-power-of-two size, unsupported",  // 6
517
        };
518
 
519
        initprintf("%s cache miss: %s\n", modelp?"Skin":"Texture", error_msgs[err]);
520
    }
521
 
522
    return 0;
523
}
524
 
3763 terminx 525
#undef READTEXHEADER_FAILURE
3758 terminx 526
#define WRITEX_FAIL_ON_ERROR() if (bglGetError() != GL_NO_ERROR) goto failure
527
 
528
void texcache_writetex(const char *fn, int32_t len, int32_t dameth, char effect, texcacheheader *head)
529
{
4546 hendricks2 530
    static GLint glGetTexLevelParameterivOK = GL_TRUE;
3758 terminx 531
    char cachefn[BMAX_PATH];
532
    char *pic = NULL, *packbuf = NULL;
533
    void *midbuf = NULL;
534
    uint32_t alloclen=0, level;
535
    uint32_t padx=0, pady=0;
536
    GLint gi;
537
    int32_t offset = 0;
538
 
539
    if (!texcache_enabled()) return;
540
 
541
    gi = GL_FALSE;
542
    bglGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED_ARB, &gi);
543
    if (gi != GL_TRUE)
544
    {
4546 hendricks2 545
        if (glGetTexLevelParameterivOK == GL_TRUE)
546
        {
547
            OSD_Printf("Error: glGetTexLevelParameteriv returned GL_FALSE!\n");
548
            glGetTexLevelParameterivOK = GL_FALSE;
549
        }
3758 terminx 550
        return;
551
    }
552
 
3781 terminx 553
    Blseek(texcache.filehandle, 0, BSEEK_END);
3758 terminx 554
 
3781 terminx 555
    offset = Blseek(texcache.filehandle, 0, BSEEK_CUR);
3758 terminx 556
    //    OSD_Printf("Caching %s, offset 0x%x\n", cachefn, offset);
557
 
558
    Bmemcpy(head->magic, TEXCACHEMAGIC, 4);   // sizes are set by caller
559
 
4488 helixhorne 560
    if (glusetexcache == 2)
561
        head->flags |= CACHEAD_COMPRESSED;
3758 terminx 562
 
563
    // native -> external (little-endian)
564
    head->xdim = B_LITTLE32(head->xdim);
565
    head->ydim = B_LITTLE32(head->ydim);
566
    head->flags = B_LITTLE32(head->flags);
567
    head->quality = B_LITTLE32(head->quality);
568
 
3781 terminx 569
    if (Bwrite(texcache.filehandle, head, sizeof(texcacheheader)) != sizeof(texcacheheader)) goto failure;
3758 terminx 570
 
571
    CLEAR_GL_ERRORS();
572
 
573
    for (level = 0; level==0 || (padx > 1 || pady > 1); level++)
574
    {
575
        uint32_t miplen;
576
        texcachepicture pict;
577
 
578
        bglGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_COMPRESSED_ARB, &gi); WRITEX_FAIL_ON_ERROR();
579
        if (gi != GL_TRUE) goto failure;   // an uncompressed mipmap
580
        bglGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_INTERNAL_FORMAT, &gi); WRITEX_FAIL_ON_ERROR();
581
 
582
#ifdef __APPLE__
583
        if (pr_ati_textureformat_one && gi == 1) gi = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
584
#endif
585
        // native -> external (little endian)
586
        pict.format = B_LITTLE32(gi);
587
        bglGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_WIDTH, &gi); WRITEX_FAIL_ON_ERROR();
588
        padx = gi; pict.xdim = B_LITTLE32(gi);
589
        bglGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_HEIGHT, &gi); WRITEX_FAIL_ON_ERROR();
590
        pady = gi; pict.ydim = B_LITTLE32(gi);
591
        bglGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_BORDER, &gi); WRITEX_FAIL_ON_ERROR();
592
        pict.border = B_LITTLE32(gi);
593
        bglGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_DEPTH, &gi); WRITEX_FAIL_ON_ERROR();
594
        pict.depth = B_LITTLE32(gi);
595
        bglGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB, &gi); WRITEX_FAIL_ON_ERROR();
596
        miplen = gi; pict.size = B_LITTLE32(gi);
597
 
598
        if (alloclen < miplen)
599
        {
4491 helixhorne 600
            pic = (char *)Xrealloc(pic, miplen);
3758 terminx 601
            alloclen = miplen;
4666 terminx 602
            packbuf = (char *)Xrealloc(packbuf, alloclen);
4491 helixhorne 603
            midbuf = (void *)Xrealloc(midbuf, miplen);
3758 terminx 604
        }
605
 
606
        bglGetCompressedTexImageARB(GL_TEXTURE_2D, level, pic); WRITEX_FAIL_ON_ERROR();
607
 
3781 terminx 608
        if (Bwrite(texcache.filehandle, &pict, sizeof(texcachepicture)) != sizeof(texcachepicture)) goto failure;
609
        if (dxtfilter(texcache.filehandle, &pict, pic, midbuf, packbuf, miplen)) goto failure;
3758 terminx 610
    }
611
 
612
    {
613
        texcacheindex *t;
3781 terminx 614
        int32_t i = hash_find(&texcache.hashes, texcache_calcid(cachefn, fn, len, dameth, effect));
3758 terminx 615
        if (i > -1)
616
        {
617
            // update an existing entry
4666 terminx 618
            t = texcache.iptrs[i];
3758 terminx 619
            t->offset = offset;
3781 terminx 620
            t->len = Blseek(texcache.filehandle, 0, BSEEK_CUR) - t->offset;
3758 terminx 621
            /*initprintf("%s %d got a match for %s offset %d\n",__FILE__, __LINE__, cachefn,offset);*/
622
        }
623
        else
624
        {
3781 terminx 625
            t = texcache.currentindex;
3758 terminx 626
            Bstrcpy(t->name, cachefn);
627
            t->offset = offset;
3781 terminx 628
            t->len = Blseek(texcache.filehandle, 0, BSEEK_CUR) - t->offset;
4491 helixhorne 629
            t->next = (texcacheindex *)Xcalloc(1, sizeof(texcacheindex));
3758 terminx 630
 
3781 terminx 631
            hash_add(&texcache.hashes, cachefn, texcache.numentries, 0);
4666 terminx 632
            if (++texcache.numentries > texcache.iptrcnt)
633
            {
634
                texcache.iptrcnt += 512;
635
                texcache.iptrs = (texcacheindex **) Xrealloc(texcache.iptrs, sizeof(intptr_t) * texcache.iptrcnt);
636
            }
637
            texcache.iptrs[texcache.numentries-1] = t;
3781 terminx 638
            texcache.currentindex = t->next;
3758 terminx 639
        }
640
 
3781 terminx 641
        if (texcache.index)
3758 terminx 642
        {
3781 terminx 643
            fseek(texcache.index, 0, BSEEK_END);
644
            Bfprintf(texcache.index, "%s %d %d\n", t->name, t->offset, t->len);
3758 terminx 645
        }
646
        else OSD_Printf("wtf?\n");
647
    }
648
 
649
    goto success;
650
 
651
failure:
652
    initprintf("ERROR: cache failure!\n");
3781 terminx 653
    texcache.currentindex->offset = 0;
654
    Bmemset(texcache.currentindex->name,0,sizeof(texcache.currentindex->name));
3758 terminx 655
 
656
success:
657
    TEXCACHE_FREEBUFS();
658
}
659
 
3763 terminx 660
#undef WRITEX_FAIL_ON_ERROR
3758 terminx 661
 
662
static void texcache_setuptexture(int32_t *doalloc, GLuint *glpic)
663
{
664
    if (*doalloc&1)
665
    {
666
        bglGenTextures(1,glpic);  //# of textures (make OpenGL allocate structure)
667
        *doalloc |= 2;  // prevents bglGenTextures being called again if we fail in here
668
    }
669
    bglBindTexture(GL_TEXTURE_2D,*glpic);
670
}
671
 
672
static int32_t texcache_loadmips(const texcacheheader *head, GLenum *glerr, int32_t *xsiz, int32_t *ysiz)
673
{
674
    int32_t level;
675
    texcachepicture pict;
676
    char *pic = NULL, *packbuf = NULL;
677
    void *midbuf = NULL;
678
    int32_t alloclen=0;
679
 
680
    for (level = 0; level==0 || (pict.xdim > 1 || pict.ydim > 1); level++)
681
    {
682
        GLint format;
683
 
684
        if (texcache_readdata(&pict, sizeof(texcachepicture)))
685
        {
686
            TEXCACHE_FREEBUFS();
687
            return TEXCACHERR_BUFFERUNDERRUN;
688
        }
689
 
690
        // external (little endian) -> native
691
        pict.size = B_LITTLE32(pict.size);
692
        pict.format = B_LITTLE32(pict.format);
693
        pict.xdim = B_LITTLE32(pict.xdim);
694
        pict.ydim = B_LITTLE32(pict.ydim);
695
        pict.border = B_LITTLE32(pict.border);
696
        pict.depth = B_LITTLE32(pict.depth);
697
 
698
        if (level == 0)
699
        {
700
            if (xsiz) *xsiz = pict.xdim;
701
            if (ysiz) *ysiz = pict.ydim;
702
        }
703
 
704
        if (alloclen < pict.size)
705
        {
4491 helixhorne 706
            pic = (char *)Xrealloc(pic, pict.size);
3758 terminx 707
            alloclen = pict.size;
4491 helixhorne 708
            packbuf = (char *)Xrealloc(packbuf, alloclen+16);
709
            midbuf = (void *)Xrealloc(midbuf, pict.size);
3758 terminx 710
        }
711
 
4488 helixhorne 712
        if (dedxtfilter(texcache.filehandle, &pict, pic, midbuf, packbuf,
713
                        (head->flags & CACHEAD_COMPRESSED)!=0))
3758 terminx 714
        {
715
            TEXCACHE_FREEBUFS();
716
            return TEXCACHERR_DEDXT;
717
        }
718
 
719
        bglCompressedTexImage2DARB(GL_TEXTURE_2D,level,pict.format,pict.xdim,pict.ydim,pict.border,pict.size,pic);
720
        if ((*glerr=bglGetError()) != GL_NO_ERROR)
721
        {
722
            TEXCACHE_FREEBUFS();
723
            return TEXCACHERR_COMPTEX;
724
        }
725
 
726
        bglGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_INTERNAL_FORMAT, &format);
727
        if ((*glerr = bglGetError()) != GL_NO_ERROR)
728
        {
729
            TEXCACHE_FREEBUFS();
730
            return TEXCACHERR_GETTEXLEVEL;
731
        }
732
 
733
        if (pict.format != format)
734
        {
735
            OSD_Printf("gloadtile_cached: invalid texture cache file format %d %d\n", pict.format, format);
736
            TEXCACHE_FREEBUFS();
737
            return -1;
738
        }
739
    }
740
 
741
    TEXCACHE_FREEBUFS();
742
    return 0;
743
}
744
 
745
int32_t texcache_loadskin(const texcacheheader *head, int32_t *doalloc, GLuint *glpic, int32_t *xsiz, int32_t *ysiz)
746
{
747
    int32_t err=0;
748
    GLenum glerr=GL_NO_ERROR;
749
 
750
    texcache_setuptexture(doalloc, glpic);
751
 
752
    CLEAR_GL_ERRORS();
753
 
754
    if ((err = texcache_loadmips(head, &glerr, xsiz, ysiz)))
755
    {
756
        if (err > 0)
3781 terminx 757
            initprintf("texcache_loadskin: %s  (glerr=%x)\n", texcache_errorstr[err], glerr);
3758 terminx 758
 
759
        return -1;
760
    }
761
 
762
    return 0;
763
}
764
 
765
int32_t texcache_loadtile(const texcacheheader *head, int32_t *doalloc, pthtyp *pth)
766
{
767
    int32_t err=0;
768
    GLenum glerr=GL_NO_ERROR;
769
 
770
    texcache_setuptexture(doalloc, &pth->glpic);
771
 
4639 terminx 772
    pth->siz.x = head->xdim;
773
    pth->siz.y = head->ydim;
3758 terminx 774
 
775
    CLEAR_GL_ERRORS();
776
 
777
    if ((err = texcache_loadmips(head, &glerr, NULL, NULL)))
778
    {
779
        if (err > 0)
3781 terminx 780
            initprintf("texcache_loadtile: %s  (glerr=%x)\n", texcache_errorstr[err], glerr);
3758 terminx 781
 
782
        return -1;
783
    }
784
 
785
    return 0;
786
}
3761 terminx 787
 
788
void texcache_setupmemcache(void)
789
{
3781 terminx 790
    if (!glusememcache || texcache.memcache.noalloc || !texcache_enabled())
3761 terminx 791
        return;
792
 
3781 terminx 793
    texcache.memcache.size = Bfilelength(texcache.filehandle);
3761 terminx 794
 
3781 terminx 795
    if (texcache.memcache.size <= 0)
3761 terminx 796
        return;
797
 
3781 terminx 798
    texcache.memcache.ptr = (uint8_t *)Brealloc(texcache.memcache.ptr, texcache.memcache.size);
3761 terminx 799
 
3781 terminx 800
    if (!texcache.memcache.ptr)
3761 terminx 801
    {
3786 helixhorne 802
        initprintf("Failed allocating %d bytes for memcache, disabling memcache.\n", (int)texcache.memcache.size);
3763 terminx 803
        texcache_clearmemcache();
3781 terminx 804
        texcache.memcache.noalloc = 1;
3761 terminx 805
        return;
806
    }
807
 
3786 helixhorne 808
    if (Bread(texcache.filehandle, texcache.memcache.ptr, texcache.memcache.size) != (bssize_t)texcache.memcache.size)
3761 terminx 809
    {
810
        initprintf("Failed reading texcache into memcache!\n");
3763 terminx 811
        texcache_clearmemcache();
3781 terminx 812
        texcache.memcache.noalloc = 1;
3761 terminx 813
    }
814
}
815
 
3904 terminx 816
#endif