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 |