Subversion Repositories eduke32

Rev

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