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