]> icculus.org git repositories - divverent/darkplaces.git/blob - gl_textures.c
fix fatal bugs in S_RawSamples_Enqueue and Dequeue relating to mis-detected buffer...
[divverent/darkplaces.git] / gl_textures.c
1
2 #include "quakedef.h"
3
4 cvar_t  r_max_size = {CVAR_SAVE, "r_max_size", "2048"};
5 cvar_t  r_max_scrapsize = {CVAR_SAVE, "r_max_scrapsize", "256"};
6 cvar_t  r_picmip = {CVAR_SAVE, "r_picmip", "0"};
7 cvar_t  r_lerpimages = {CVAR_SAVE, "r_lerpimages", "1"};
8 cvar_t  r_precachetextures = {CVAR_SAVE, "r_precachetextures", "1"};
9
10 int             gl_filter_min = GL_LINEAR_MIPMAP_LINEAR;
11 int             gl_filter_mag = GL_LINEAR;
12
13
14 static mempool_t *texturemempool;
15 static mempool_t *texturedatamempool;
16 static mempool_t *textureprocessingmempool;
17
18 // note: this must not conflict with TEXF_ flags in r_textures.h
19 // cleared when a texture is uploaded
20 #define GLTEXF_UPLOAD 0x00010000
21 // texture generated by code, also causes permanent GLTEXF_UPLOAD effect
22 #define GLTEXF_PROCEDURAL 0x00020000
23 // bitmask for mismatch checking
24 #define GLTEXF_IMPORTANTBITS (GLTEXF_PROCEDURAL)
25 // set when image is uploaded and freed
26 #define GLTEXF_DESTROYED 0x00040000
27
28 // size of images which hold fragment textures, ignores picmip and max_size
29 //#define BLOCK_SIZE 256
30 static int block_size;
31
32 // really this number only governs gltexnuminuse
33 #define MAX_GLTEXTURES 65536
34
35 // since there is only one set of GL texture numbers, we have to track them
36 // globally, everything else is per texture pool
37 static qbyte *gltexnuminuse;
38
39 typedef struct
40 {
41         int textype;
42         int inputbytesperpixel;
43         int internalbytesperpixel;
44         int glformat;
45         int glinternalformat;
46         int align;
47 }
48 textypeinfo_t;
49
50 static textypeinfo_t textype_qpalette       = {TEXTYPE_QPALETTE, 1, 4, GL_RGBA, 3, 1};
51 static textypeinfo_t textype_rgb            = {TEXTYPE_RGB     , 3, 3, GL_RGB , 3, 3};
52 static textypeinfo_t textype_rgba           = {TEXTYPE_RGBA    , 4, 4, GL_RGBA, 3, 1};
53 static textypeinfo_t textype_qpalette_alpha = {TEXTYPE_QPALETTE, 1, 4, GL_RGBA, 4, 1};
54 static textypeinfo_t textype_rgba_alpha     = {TEXTYPE_RGBA    , 4, 4, GL_RGBA, 4, 1};
55
56 // a tiling texture (most common type)
57 #define GLIMAGETYPE_TILE 0
58 // a fragments texture (contains one or more fragment textures)
59 #define GLIMAGETYPE_FRAGMENTS 1
60
61 // a gltextureimage can have one (or more if fragments) gltextures inside
62 typedef struct gltextureimage_s
63 {
64         struct gltextureimage_s *imagechain;
65         int texturecount;
66         int type; // one of the GLIMAGETYPE_ values
67         int texnum; // GL texture slot number
68         int width, height;
69         int bytesperpixel; // bytes per pixel
70         int glformat; // GL_RGB or GL_RGBA
71         int glinternalformat; // 3 or 4
72         int flags;
73         short *blockallocation; // fragment allocation
74 }
75 gltextureimage_t;
76
77 typedef struct gltexture_s
78 {
79         // pointer to texturepool (check this to see if the texture is allocated)
80         struct gltexturepool_s *pool;
81         // pointer to next texture in texturepool chain
82         struct gltexture_s *chain;
83         // pointer into gltextureimage array
84         gltextureimage_t *image;
85         // name of the texture (this might be removed someday), no duplicates
86         char *identifier;
87         // location in the image, and size
88         int x, y, width, height;
89         // copy of the original texture supplied to the upload function, for re-uploading or deferred uploads (non-precached)
90         qbyte *inputtexels;
91         // to identify cache mismatchs (this might be removed someday)
92         int crc;
93         // flags supplied to the LoadTexture/ProceduralTexture functions
94         // (might be altered to remove TEXF_ALPHA), and GLTEXF_ private flags
95         int flags;
96         // procedural texture generation function, called once per frame if the texture is used
97         int (*generate)(qbyte *buffer, int width, int height, void *parameterdata, int parameterdatasize);
98         // data provided to generate, persistent from call to call
99         qbyte *proceduraldata;
100         // size of data
101         int proceduraldatasize;
102         // used only to avoid updating the texture more than once per frame
103         int proceduralframecount;
104         // pointer to one of the textype_ structs
105         textypeinfo_t *textype;
106 }
107 gltexture_t;
108
109 #define TEXTUREPOOL_SENTINEL 0xC0DEDBAD
110
111 typedef struct gltexturepool_s
112 {
113         int sentinel;
114         struct gltextureimage_s *imagechain;
115         struct gltexture_s *gltchain;
116         struct gltexturepool_s *next;
117 }
118 gltexturepool_t;
119
120 static gltexturepool_t *gltexturepoolchain = NULL;
121
122 static qbyte *resizebuffer = NULL, *colorconvertbuffer;
123 static int resizebuffersize = 0;
124 static qbyte *texturebuffer;
125 static int texturebuffersize = 0;
126
127 static int realmaxsize = 0;
128
129 static textypeinfo_t *R_GetTexTypeInfo(int textype, int flags)
130 {
131         if (flags & TEXF_ALPHA)
132         {
133                 switch(textype)
134                 {
135                 case TEXTYPE_QPALETTE:
136                         return &textype_qpalette_alpha;
137                 case TEXTYPE_RGB:
138                         Host_Error("R_GetTexTypeInfo: RGB format has no alpha, TEXF_ALPHA not allowed\n");
139                         return NULL;
140                 case TEXTYPE_RGBA:
141                         return &textype_rgba_alpha;
142                 default:
143                         Host_Error("R_GetTexTypeInfo: unknown texture format\n");
144                         return NULL;
145                 }
146         }
147         else
148         {
149                 switch(textype)
150                 {
151                 case TEXTYPE_QPALETTE:
152                         return &textype_qpalette;
153                 case TEXTYPE_RGB:
154                         return &textype_rgb;
155                 case TEXTYPE_RGBA:
156                         return &textype_rgba;
157                 default:
158                         Host_Error("R_GetTexTypeInfo: unknown texture format\n");
159                         return NULL;
160                 }
161         }
162 }
163
164 static void R_UploadTexture(gltexture_t *t);
165
166 static void R_PrecacheTexture(gltexture_t *glt)
167 {
168         int precache;
169         precache = false;
170         if (glt->flags & TEXF_ALWAYSPRECACHE)
171                 precache = true;
172         else if (r_precachetextures.integer >= 2)
173                 precache = true;
174         else if (r_precachetextures.integer >= 1)
175                 if (glt->flags & TEXF_PRECACHE)
176                         precache = true;
177
178         if (precache)
179                 R_UploadTexture(glt);
180 }
181
182 int R_GetTexture(rtexture_t *rt)
183 {
184         gltexture_t *glt;
185         if (!rt)
186                 return 0;
187         glt = (gltexture_t *)rt;
188         if (glt->flags & (GLTEXF_UPLOAD | GLTEXF_PROCEDURAL))
189         {
190                 if (glt->flags & GLTEXF_PROCEDURAL)
191                 {
192                         if (glt->proceduralframecount != r_framecount)
193                         {
194                                 glt->proceduralframecount = r_framecount;
195                                 R_UploadTexture(glt);
196                         }
197                 }
198                 else
199                         R_UploadTexture(glt);
200         }
201         return glt->image->texnum;
202 }
203
204 void R_FreeTexture(rtexture_t *rt)
205 {
206         gltexture_t *glt, **gltpointer;
207         gltextureimage_t *image, **gltimagepointer;
208         GLuint texnum;
209
210         glt = (gltexture_t *)rt;
211         if (glt == NULL)
212                 Host_Error("R_FreeTexture: texture == NULL\n");
213
214         for (gltpointer = &glt->pool->gltchain;*gltpointer && *gltpointer != glt;gltpointer = &(*gltpointer)->chain);
215         if (*gltpointer == glt)
216                 *gltpointer = glt->chain;
217         else
218                 Host_Error("R_FreeTexture: texture \"%s\" not linked in pool\n", glt->identifier);
219
220         // note: if freeing a fragment texture, this will not make the claimed
221         // space available for new textures unless all other fragments in the
222         // image are also freed
223         image = glt->image;
224         image->texturecount--;
225         if (image->texturecount < 1)
226         {
227                 for (gltimagepointer = &glt->pool->imagechain;*gltimagepointer && *gltimagepointer != image;gltimagepointer = &(*gltimagepointer)->imagechain);
228                 if (*gltimagepointer == image)
229                         *gltimagepointer = image->imagechain;
230                 else
231                         Host_Error("R_FreeTexture: image not linked in pool\n");
232                 if (image->texnum)
233                 {
234                         texnum = image->texnum;
235                         gltexnuminuse[image->texnum] = 0;
236                         qglDeleteTextures(1, &texnum);
237                 }
238                 if (image->blockallocation)
239                         Mem_Free(image->blockallocation);
240                 Mem_Free(image);
241         }
242
243         if (glt->identifier)
244                 Mem_Free(glt->identifier);
245         if (glt->inputtexels)
246                 Mem_Free(glt->inputtexels);
247         if (glt->proceduraldata)
248                 Mem_Free(glt->proceduraldata);
249         Mem_Free(glt);
250 }
251
252 static gltexture_t *R_FindTexture (gltexturepool_t *pool, char *identifier)
253 {
254         gltexture_t     *glt;
255
256         if (!identifier)
257                 return NULL;
258
259         for (glt = pool->gltchain;glt;glt = glt->chain)
260                 if (glt->identifier && !strcmp (identifier, glt->identifier))
261                         return glt;
262
263         return NULL;
264 }
265
266 rtexturepool_t *R_AllocTexturePool(void)
267 {
268         gltexturepool_t *pool;
269         if (texturemempool == NULL)
270                 return NULL;
271         pool = Mem_Alloc(texturemempool, sizeof(gltexturepool_t));
272         if (pool == NULL)
273                 return NULL;
274         pool->next = gltexturepoolchain;
275         gltexturepoolchain = pool;
276         pool->sentinel = TEXTUREPOOL_SENTINEL;
277         return (rtexturepool_t *)pool;
278 }
279
280 void R_FreeTexturePool(rtexturepool_t **rtexturepool)
281 {
282         gltexturepool_t *pool, **poolpointer;
283         if (rtexturepool == NULL)
284                 return;
285         if (*rtexturepool == NULL)
286                 return;
287         pool = (gltexturepool_t *)(*rtexturepool);
288         *rtexturepool = NULL;
289         if (pool->sentinel != TEXTUREPOOL_SENTINEL)
290                 Host_Error("R_FreeTexturePool: pool already freed\n");
291         for (poolpointer = &gltexturepoolchain;*poolpointer && *poolpointer != pool;poolpointer = &(*poolpointer)->next);
292         if (*poolpointer == pool)
293                 *poolpointer = pool->next;
294         else
295                 Host_Error("R_FreeTexturePool: pool not linked\n");
296         while (pool->gltchain)
297                 R_FreeTexture((rtexture_t *)pool->gltchain);
298         if (pool->imagechain)
299                 Sys_Error("R_FreeTexturePool: not all images freed\n");
300         Mem_Free(pool);
301 }
302
303
304 typedef struct
305 {
306         char *name;
307         int minification, magnification;
308 }
309 glmode_t;
310
311 static glmode_t modes[] =
312 {
313         {"GL_NEAREST", GL_NEAREST, GL_NEAREST},
314         {"GL_LINEAR", GL_LINEAR, GL_LINEAR},
315         {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
316         {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
317         {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
318         {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
319 };
320
321 extern int gl_backend_rebindtextures;
322
323 static void GL_TextureMode_f (void)
324 {
325         int i;
326         gltextureimage_t *image;
327         gltexturepool_t *pool;
328
329         if (Cmd_Argc() == 1)
330         {
331                 for (i = 0;i < 6;i++)
332                 {
333                         if (gl_filter_min == modes[i].minification)
334                         {
335                                 Con_Printf ("%s\n", modes[i].name);
336                                 return;
337                         }
338                 }
339                 Con_Printf ("current filter is unknown???\n");
340                 return;
341         }
342
343         for (i = 0;i < 6;i++)
344                 if (!Q_strcasecmp (modes[i].name, Cmd_Argv(1) ) )
345                         break;
346         if (i == 6)
347         {
348                 Con_Printf ("bad filter name\n");
349                 return;
350         }
351
352         gl_filter_min = modes[i].minification;
353         gl_filter_mag = modes[i].magnification;
354
355         // change all the existing mipmap texture objects
356         // FIXME: force renderer(/client/something?) restart instead?
357         for (pool = gltexturepoolchain;pool;pool = pool->next)
358         {
359                 for (image = pool->imagechain;image;image = image->imagechain)
360                 {
361                         // only update already uploaded images
362                         if (!(image->flags & GLTEXF_UPLOAD))
363                         {
364                                 qglBindTexture(GL_TEXTURE_2D, image->texnum);
365                                 if (image->flags & TEXF_MIPMAP)
366                                         qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
367                                 else
368                                         qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_mag);
369                                 qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_mag);
370                         }
371                 }
372         }
373         gl_backend_rebindtextures = true;
374 }
375
376 static int R_CalcTexelDataSize (gltexture_t *glt)
377 {
378         int width2, height2, size;
379         if (glt->flags & TEXF_FRAGMENT)
380                 size = glt->width * glt->height;
381         else
382         {
383                 if (r_max_size.integer > realmaxsize)
384                         Cvar_SetValue("r_max_size", realmaxsize);
385                 // calculate final size
386                 for (width2 = 1;width2 < glt->width;width2 <<= 1);
387                 for (height2 = 1;height2 < glt->height;height2 <<= 1);
388                 for (width2 >>= r_picmip.integer;width2 > r_max_size.integer;width2 >>= 1);
389                 for (height2 >>= r_picmip.integer;height2 > r_max_size.integer;height2 >>= 1);
390                 if (width2 < 1) width2 = 1;
391                 if (height2 < 1) height2 = 1;
392
393                 size = 0;
394                 if (glt->flags & TEXF_MIPMAP)
395                 {
396                         while (width2 > 1 || height2 > 1)
397                         {
398                                 size += width2 * height2;
399                                 if (width2 > 1)
400                                         width2 >>= 1;
401                                 if (height2 > 1)
402                                         height2 >>= 1;
403                         }
404                         size++; // count the last 1x1 mipmap
405                 }
406                 else
407                         size = width2*height2;
408         }
409         size *= glt->textype->internalbytesperpixel;
410
411         return size;
412 }
413
414 void R_TextureStats_PrintTotal(void)
415 {
416         int glsize, inputsize, total = 0, totalt = 0, totalp = 0, loaded = 0, loadedt = 0, loadedp = 0;
417         gltexture_t *glt;
418         gltexturepool_t *pool;
419         for (pool = gltexturepoolchain;pool;pool = pool->next)
420         {
421                 for (glt = pool->gltchain;glt;glt = glt->chain)
422                 {
423                         glsize = R_CalcTexelDataSize(glt);
424                         inputsize = glt->width * glt->height * glt->textype->inputbytesperpixel;
425
426                         total++;
427                         totalt += glsize;
428                         totalp += inputsize;
429                         if (!(glt->flags & GLTEXF_UPLOAD))
430                         {
431                                 loaded++;
432                                 loadedt += glsize;
433                                 loadedp += inputsize;
434                         }
435                 }
436         }
437         Con_Printf("total: %i (%.3fMB, %.3fMB original), uploaded %i (%.3fMB, %.3fMB original), upload on demand %i (%.3fMB, %.3fMB original)\n", total, totalt / 1048576.0, totalp / 1048576.0, loaded, loadedt / 1048576.0, loadedp / 1048576.0, total - loaded, (totalt - loadedt) / 1048576.0, (totalp - loadedp) / 1048576.0);
438 }
439
440 static void R_TextureStats_f(void)
441 {
442         int loaded;
443         gltexture_t *glt;
444         gltexturepool_t *pool;
445         Con_Printf("glsize input crc  loaded mip alpha name\n");
446         for (pool = gltexturepoolchain;pool;pool = pool->next)
447         {
448                 for (glt = pool->gltchain;glt;glt = glt->chain)
449                 {
450                         loaded = !(glt->flags & GLTEXF_UPLOAD);
451                         if (glt->flags & GLTEXF_PROCEDURAL)
452                                 Con_Printf("%c%4i%c %4i  PROC %s %s %s %s\n"  , loaded ? '[' : ' ', (R_CalcTexelDataSize(glt) + 1023) / 1024, loaded ? ']' : ' ',                               (glt->width * glt->height * glt->textype->inputbytesperpixel + 1023) / 1024,                                         loaded ? "loaded" : "      ", (glt->flags & TEXF_MIPMAP) ? "mip" : "   ", (glt->flags & TEXF_ALPHA) ? "alpha" : "     ", glt->identifier ? glt->identifier : "<unnamed>");
453                         else
454                                 Con_Printf("%c%4i%c%c%4i%c %04X %s %s %s %s\n", loaded ? '[' : ' ', (R_CalcTexelDataSize(glt) + 1023) / 1024, loaded ? ']' : ' ', glt->inputtexels ? '[' : ' ', (glt->width * glt->height * glt->textype->inputbytesperpixel + 1023) / 1024, glt->inputtexels ? ']' : ' ', glt->crc, loaded ? "loaded" : "      ", (glt->flags & TEXF_MIPMAP) ? "mip" : "   ", (glt->flags & TEXF_ALPHA) ? "alpha" : "     ", glt->identifier ? glt->identifier : "<unnamed>");
455                 }
456                 Con_Printf("pool %10p\n", pool);
457         }
458         R_TextureStats_PrintTotal();
459 }
460
461 char engineversion[40];
462
463 static void r_textures_start(void)
464 {
465         // deal with size limits of various drivers (3dfx in particular)
466         qglGetIntegerv(GL_MAX_TEXTURE_SIZE, &realmaxsize);
467         CHECKGLERROR
468
469         // use the largest scrap texture size we can (not sure if this is really a good idea)
470         for (block_size = 1;block_size < realmaxsize && block_size < r_max_scrapsize.integer;block_size <<= 1);
471
472         texturemempool = Mem_AllocPool("Texture Info");
473         texturedatamempool = Mem_AllocPool("Texture Storage (not yet uploaded)");
474         textureprocessingmempool = Mem_AllocPool("Texture Processing Buffers");
475         gltexnuminuse = Mem_Alloc(texturemempool, MAX_GLTEXTURES);
476 }
477
478 static void r_textures_shutdown(void)
479 {
480         rtexturepool_t *temp;
481         while(gltexturepoolchain)
482         {
483                 temp = (rtexturepool_t *) gltexturepoolchain;
484                 R_FreeTexturePool(&temp);
485         }
486
487         resizebuffersize = 0;
488         texturebuffersize = 0;
489         resizebuffer = NULL;
490         colorconvertbuffer = NULL;
491         texturebuffer = NULL;
492         gltexnuminuse = NULL;
493         Mem_FreePool(&texturemempool);
494         Mem_FreePool(&texturedatamempool);
495         Mem_FreePool(&textureprocessingmempool);
496 }
497
498 static void r_textures_newmap(void)
499 {
500 }
501
502 void R_Textures_Init (void)
503 {
504         Cmd_AddCommand ("gl_texturemode", &GL_TextureMode_f);
505         Cmd_AddCommand("r_texturestats", R_TextureStats_f);
506         Cvar_RegisterVariable (&r_max_scrapsize);
507         Cvar_RegisterVariable (&r_max_size);
508         Cvar_RegisterVariable (&r_picmip);
509         Cvar_RegisterVariable (&r_lerpimages);
510         Cvar_RegisterVariable (&r_precachetextures);
511         gltexnuminuse = NULL;
512
513         R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap);
514 }
515
516 static void R_Upload(gltexture_t *glt, qbyte *data)
517 {
518         int mip, width, height, internalformat;
519         qbyte *prevbuffer;
520         prevbuffer = data;
521
522         qglBindTexture(GL_TEXTURE_2D, glt->image->texnum);
523         CHECKGLERROR
524
525         gl_backend_rebindtextures = true;
526
527         glt->flags &= ~GLTEXF_UPLOAD;
528
529         if (glt->flags & TEXF_FRAGMENT)
530         {
531                 if (resizebuffersize < glt->image->width * glt->image->height * glt->image->bytesperpixel)
532                 {
533                         resizebuffersize = glt->image->width * glt->image->height * glt->image->bytesperpixel;
534                         if (resizebuffer)
535                                 Mem_Free(resizebuffer);
536                         if (colorconvertbuffer)
537                                 Mem_Free(colorconvertbuffer);
538                         resizebuffer = Mem_Alloc(textureprocessingmempool, resizebuffersize);
539                         colorconvertbuffer = Mem_Alloc(textureprocessingmempool, resizebuffersize);
540                         if (!resizebuffer || !colorconvertbuffer)
541                                 Host_Error("R_Upload: out of memory\n");
542                 }
543
544                 if (glt->image->flags & GLTEXF_UPLOAD)
545                 {
546                         Con_DPrintf("uploaded new fragments image\n");
547                         glt->image->flags &= ~GLTEXF_UPLOAD;
548                         memset(resizebuffer, 255, glt->image->width * glt->image->height * glt->image->bytesperpixel);
549                         qglTexImage2D (GL_TEXTURE_2D, 0, glt->image->glinternalformat, glt->image->width, glt->image->height, 0, glt->image->glformat, GL_UNSIGNED_BYTE, resizebuffer);
550                         CHECKGLERROR
551                         qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_mag);
552                         CHECKGLERROR
553                         qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_mag);
554                         CHECKGLERROR
555                 }
556
557                 if (prevbuffer == NULL)
558                 {
559                         memset(resizebuffer, 255, glt->width * glt->height * glt->image->bytesperpixel);
560                         prevbuffer = resizebuffer;
561                 }
562                 else if (glt->textype->textype == TEXTYPE_QPALETTE)
563                 {
564                         // promote paletted to RGBA, so we only have to worry about RGB and
565                         // RGBA in the rest of this code
566                         Image_Copy8bitRGBA(prevbuffer, colorconvertbuffer, glt->width * glt->height, d_8to24table);
567                         prevbuffer = colorconvertbuffer;
568                 }
569
570                 qglTexSubImage2D(GL_TEXTURE_2D, 0, glt->x, glt->y, glt->width, glt->height, glt->image->glformat, GL_UNSIGNED_BYTE, prevbuffer);
571                 CHECKGLERROR
572                 return;
573         }
574
575         glt->image->flags &= ~GLTEXF_UPLOAD;
576
577         // these are rounded up versions of the size to do better resampling
578         for (width = 1;width < glt->width;width <<= 1);
579         for (height = 1;height < glt->height;height <<= 1);
580
581         if (resizebuffersize < width * height * glt->image->bytesperpixel)
582         {
583                 resizebuffersize = width * height * glt->image->bytesperpixel;
584                 if (resizebuffer)
585                         Mem_Free(resizebuffer);
586                 if (colorconvertbuffer)
587                         Mem_Free(colorconvertbuffer);
588                 resizebuffer = Mem_Alloc(textureprocessingmempool, resizebuffersize);
589                 colorconvertbuffer = Mem_Alloc(textureprocessingmempool, resizebuffersize);
590                 if (!resizebuffer || !colorconvertbuffer)
591                         Host_Error("R_Upload: out of memory\n");
592         }
593
594         if (prevbuffer == NULL)
595         {
596                 width = glt->image->width;
597                 height = glt->image->height;
598                 memset(resizebuffer, 255, width * height * glt->image->bytesperpixel);
599                 prevbuffer = resizebuffer;
600         }
601         else
602         {
603                 if (glt->textype->textype == TEXTYPE_QPALETTE)
604                 {
605                         // promote paletted to RGBA, so we only have to worry about RGB and
606                         // RGBA in the rest of this code
607                         Image_Copy8bitRGBA(prevbuffer, colorconvertbuffer, glt->width * glt->height, d_8to24table);
608                         prevbuffer = colorconvertbuffer;
609                 }
610
611                 if (glt->width != width || glt->height != height)
612                 {
613                         Image_Resample(prevbuffer, glt->width, glt->height, resizebuffer, width, height, glt->image->bytesperpixel, r_lerpimages.integer);
614                         prevbuffer = resizebuffer;
615                 }
616
617                 // apply picmip/max_size limitations
618                 while (width > glt->image->width || height > glt->image->height)
619                 {
620                         Image_MipReduce(prevbuffer, resizebuffer, &width, &height, glt->image->width, glt->image->height, glt->image->bytesperpixel);
621                         prevbuffer = resizebuffer;
622                 }
623         }
624
625         // 3 and 4 are converted by the driver to it's preferred format for the current display mode
626         internalformat = 3;
627         if (glt->flags & TEXF_ALPHA)
628                 internalformat = 4;
629
630         mip = 0;
631         qglTexImage2D(GL_TEXTURE_2D, mip++, internalformat, width, height, 0, glt->image->glformat, GL_UNSIGNED_BYTE, prevbuffer);
632         CHECKGLERROR
633         if (glt->flags & TEXF_MIPMAP)
634         {
635                 while (width > 1 || height > 1)
636                 {
637                         Image_MipReduce(prevbuffer, resizebuffer, &width, &height, 1, 1, glt->image->bytesperpixel);
638                         prevbuffer = resizebuffer;
639
640                         qglTexImage2D(GL_TEXTURE_2D, mip++, internalformat, width, height, 0, glt->image->glformat, GL_UNSIGNED_BYTE, prevbuffer);
641                         CHECKGLERROR
642                 }
643
644                 qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
645                 CHECKGLERROR
646                 qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_mag);
647                 CHECKGLERROR
648         }
649         else
650         {
651                 qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_mag);
652                 CHECKGLERROR
653                 qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_mag);
654                 CHECKGLERROR
655         }
656 }
657
658 static void R_FindImageForTexture(gltexture_t *glt)
659 {
660         int i, j, best, best2, x, y, w, h;
661         textypeinfo_t *texinfo;
662         gltexturepool_t *pool;
663         gltextureimage_t *image, **imagechainpointer;
664         texinfo = glt->textype;
665         pool = glt->pool;
666
667         x = 0;
668         y = 0;
669         w = glt->width;
670         h = glt->height;
671         if (glt->flags & TEXF_FRAGMENT)
672         {
673                 for (imagechainpointer = &pool->imagechain;*imagechainpointer;imagechainpointer = &(*imagechainpointer)->imagechain)
674                 {
675                         image = *imagechainpointer;
676                         if (image->type != GLIMAGETYPE_FRAGMENTS)
677                                 continue;
678                         if (image->glformat != texinfo->glformat || image->glinternalformat != texinfo->glinternalformat)
679                                 continue;
680
681                         // got a fragments texture, find a place in it if we can
682                         best = block_size;
683                         for (best = block_size, i = 0;i < block_size - w;i += texinfo->align)
684                         {
685                                 for (best2 = 0, j = 0;j < w;j++)
686                                 {
687                                         if (image->blockallocation[i+j] >= best)
688                                                 break;
689                                         if (best2 < image->blockallocation[i+j])
690                                                 best2 = image->blockallocation[i+j];
691                                 }
692                                 if (j == w)
693                                 {
694                                         // this is a valid spot
695                                         x = i;
696                                         y = best = best2;
697                                 }
698                         }
699
700                         if (best + h > block_size)
701                                 continue;
702
703                         for (i = 0;i < w;i++)
704                                 image->blockallocation[x + i] = best + h;
705
706                         glt->x = x;
707                         glt->y = y;
708                         glt->image = image;
709                         image->texturecount++;
710                         return;
711                 }
712
713                 image = Mem_Alloc(texturemempool, sizeof(gltextureimage_t));
714                 if (image == NULL)
715                         Sys_Error("R_FindImageForTexture: ran out of memory\n");
716                 image->type = GLIMAGETYPE_FRAGMENTS;
717                 image->width = block_size;
718                 image->height = block_size;
719                 image->blockallocation = Mem_Alloc(texturemempool, block_size * sizeof(short));
720                 memset(image->blockallocation, 0, block_size * sizeof(short));
721
722                 x = 0;
723                 y = 0;
724                 for (i = 0;i < w;i++)
725                         image->blockallocation[x + i] = y + h;
726         }
727         else
728         {
729                 for (imagechainpointer = &pool->imagechain;*imagechainpointer;imagechainpointer = &(*imagechainpointer)->imagechain);
730
731                 image = Mem_Alloc(texturemempool, sizeof(gltextureimage_t));
732                 if (image == NULL)
733                         Sys_Error("R_FindImageForTexture: ran out of memory\n");
734                 image->type = GLIMAGETYPE_TILE;
735                 image->blockallocation = NULL;
736
737                 // calculate final size
738                 if (r_max_size.integer > realmaxsize)
739                         Cvar_SetValue("r_max_size", realmaxsize);
740                 for (image->width = 1;image->width < glt->width;image->width <<= 1);
741                 for (image->height = 1;image->height < glt->height;image->height <<= 1);
742                 for (image->width >>= r_picmip.integer;image->width > r_max_size.integer;image->width >>= 1);
743                 for (image->height >>= r_picmip.integer;image->height > r_max_size.integer;image->height >>= 1);
744                 if (image->width < 1) image->width = 1;
745                 if (image->height < 1) image->height = 1;
746         }
747         image->glinternalformat = texinfo->glinternalformat;
748         image->glformat = texinfo->glformat;
749         image->flags = (glt->flags & (TEXF_MIPMAP | TEXF_ALPHA)) | GLTEXF_UPLOAD;
750         image->bytesperpixel = texinfo->internalbytesperpixel;
751         for (i = 1;i < MAX_GLTEXTURES;i++)
752                 if (!gltexnuminuse[i])
753                         break;
754         if (i < MAX_GLTEXTURES)
755                 gltexnuminuse[image->texnum = i] = true;
756         else
757                 Sys_Error("R_FindImageForTexture: ran out of GL textures\n");
758         *imagechainpointer = image;
759         image->texturecount++;
760
761         glt->x = x;
762         glt->y = y;
763         glt->image = image;
764 }
765
766 // note: R_FindImageForTexture must be called before this
767 static void R_UploadTexture (gltexture_t *glt)
768 {
769         if (!(glt->flags & (GLTEXF_UPLOAD | GLTEXF_PROCEDURAL)))
770                 return;
771
772         if (glt->flags & GLTEXF_PROCEDURAL)
773         {
774                 if (glt->generate)
775                 {
776                         if (texturebuffersize < glt->width * glt->height * glt->textype->inputbytesperpixel)
777                         {
778                                 if (texturebuffer)
779                                         Mem_Free(texturebuffer);
780                                 texturebuffersize = glt->width * glt->height * glt->textype->inputbytesperpixel;
781                                 texturebuffer = Mem_Alloc(textureprocessingmempool, texturebuffersize);
782                         }
783
784                         glt->generate(texturebuffer, glt->width, glt->height, (void *)glt->proceduraldata, glt->proceduraldatasize);
785                 }
786         }
787         else
788         {
789                 R_Upload(glt, glt->inputtexels);
790                 if (glt->inputtexels)
791                 {
792                         Mem_Free(glt->inputtexels);
793                         glt->inputtexels = NULL;
794                         glt->flags |= GLTEXF_DESTROYED;
795                 }
796                 else if (glt->flags & GLTEXF_DESTROYED)
797                         Con_Printf("R_UploadTexture: Texture %s already uploaded and destroyed.  Can not upload original image again.  Uploaded blank texture.\n", glt->identifier);
798         }
799 }
800
801 static gltexture_t *R_SetupTexture(gltexturepool_t *pool, char *identifier, int crc, int width, int height, int flags, textypeinfo_t *texinfo, qbyte *data, int (*generate)(qbyte *buffer, int width, int height, void *proceduraldata, int proceduraldatasize), void *proceduraldata, int proceduraldatasize)
802 {
803         gltexture_t *glt;
804         glt = Mem_Alloc(texturemempool, sizeof(gltexture_t));
805         if (identifier)
806         {
807                 glt->identifier = Mem_Alloc(texturemempool, strlen(identifier)+1);
808                 strcpy (glt->identifier, identifier);
809         }
810         else
811                 glt->identifier = NULL;
812         glt->pool = pool;
813         glt->chain = pool->gltchain;
814         pool->gltchain = glt;
815         glt->crc = crc;
816         glt->width = width;
817         glt->height = height;
818         glt->flags = flags;
819         glt->textype = texinfo;
820
821         if (data)
822         {
823                 glt->inputtexels = Mem_Alloc(texturedatamempool, glt->width * glt->height * texinfo->inputbytesperpixel);
824                 if (glt->inputtexels == NULL)
825                         Sys_Error("R_SetupTexture: out of memory\n");
826                 memcpy(glt->inputtexels, data, glt->width * glt->height * texinfo->inputbytesperpixel);
827         }
828         else
829                 glt->inputtexels = NULL;
830
831         glt->generate = generate;
832         glt->proceduraldatasize = proceduraldatasize;
833         if (proceduraldatasize)
834         {
835                 glt->proceduraldata = Mem_Alloc(texturemempool, proceduraldatasize);
836                 if (glt->proceduraldata == NULL)
837                         Sys_Error("R_SetupTexture: out of memory\n");
838         }
839         else
840                 glt->proceduraldata = NULL;
841
842         R_FindImageForTexture(glt);
843         R_PrecacheTexture(glt);
844
845         return glt;
846 }
847
848 /*
849 ================
850 R_LoadTexture
851 ================
852 */
853 rtexture_t *R_LoadTexture (rtexturepool_t *rtexturepool, char *identifier, int width, int height, qbyte *data, int textype, int flags)
854 {
855         int i;
856         gltexture_t *glt;
857         gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
858         textypeinfo_t *texinfo;
859         unsigned short crc;
860
861         if (cls.state == ca_dedicated)
862                 return NULL;
863
864         texinfo = R_GetTexTypeInfo(textype, flags);
865
866         if (flags & TEXF_FRAGMENT)
867         {
868                 if (width > block_size || height > block_size)
869                         Host_Error("R_LoadTexture: fragment too big, must be no more than %dx%d\n", block_size, block_size);
870                 if ((width * texinfo->internalbytesperpixel) & 3)
871                         Host_Error("R_LoadTexture: incompatible width for fragment");
872         }
873
874         // clear the alpha flag if the texture has no transparent pixels
875         switch(textype)
876         {
877         case TEXTYPE_QPALETTE:
878                 if (flags & TEXF_ALPHA)
879                 {
880                         flags &= ~TEXF_ALPHA;
881                         for (i = 0;i < width * height;i++)
882                         {
883                                 if (data[i] == 255)
884                                 {
885                                         flags |= TEXF_ALPHA;
886                                         break;
887                                 }
888                         }
889                 }
890                 break;
891         case TEXTYPE_RGB:
892                 if (flags & TEXF_ALPHA)
893                         Host_Error("R_LoadTexture: RGB has no alpha, don't specify TEXF_ALPHA\n");
894                 break;
895         case TEXTYPE_RGBA:
896                 if (flags & TEXF_ALPHA)
897                 {
898                         flags &= ~TEXF_ALPHA;
899                         for (i = 0;i < width * height;i++)
900                         {
901                                 if (data[i * 4 + 3] < 255)
902                                 {
903                                         flags |= TEXF_ALPHA;
904                                         break;
905                                 }
906                         }
907                 }
908                 break;
909         default:
910                 Host_Error("R_LoadTexture: unknown texture type\n");
911         }
912
913         // LordHavoc: do a CRC to confirm the data really is the same as previous occurances.
914         if (data == NULL)
915                 crc = 0;
916         else
917                 crc = CRC_Block(data, width*height*texinfo->inputbytesperpixel);
918
919         // see if the texture is already present
920         if (identifier && (glt = R_FindTexture(pool, identifier)))
921         {
922                 if (crc == glt->crc && width == glt->width && height == glt->height && texinfo == glt->textype && ((flags ^ glt->flags) & TEXF_IMPORTANTBITS) == 0 && ((flags ^ glt->flags) & GLTEXF_IMPORTANTBITS) == 0)
923                 {
924                         Con_Printf("R_LoadTexture: exact match with existing texture %s\n", identifier);
925                         return (rtexture_t *)glt; // exact match, use existing
926                 }
927                 Con_Printf("R_LoadTexture: cache mismatch on %s, replacing old texture\n", identifier);
928                 R_FreeTexture((rtexture_t *)glt);
929         }
930
931         return (rtexture_t *)R_SetupTexture(pool, identifier, crc, width, height, flags | GLTEXF_UPLOAD, texinfo, data, NULL, NULL, 0);
932 }
933
934 rtexture_t *R_ProceduralTexture (rtexturepool_t *rtexturepool, char *identifier, int width, int height, int textype, int flags, int (*generate)(qbyte *buffer, int width, int height, void *proceduraldata, int proceduraldatasize), void *proceduraldata, int proceduraldatasize)
935 {
936         gltexture_t             *glt;
937         gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
938         textypeinfo_t   *texinfo;
939
940         if (cls.state == ca_dedicated)
941                 return NULL;
942
943         texinfo = R_GetTexTypeInfo(textype, flags);
944
945         if (flags & TEXF_FRAGMENT)
946         {
947                 if (width > block_size || height > block_size)
948                         Host_Error("R_ProceduralTexture: fragment too big, must be no more than %dx%d\n", block_size, block_size);
949                 if ((width * texinfo->internalbytesperpixel) & 3)
950                         Host_Error("R_ProceduralTexture: incompatible width for fragment");
951         }
952
953         // see if the texture is already present
954         if (identifier && (glt = R_FindTexture(pool, identifier)))
955         {
956                 if (width == glt->width && height == glt->height && texinfo == glt->textype && ((flags ^ glt->flags) & TEXF_IMPORTANTBITS) == 0 && ((flags ^ glt->flags) & GLTEXF_IMPORTANTBITS) == 0)
957                 {
958                         Con_Printf("R_LoadTexture: exact match with existing texture %s\n", identifier);
959                         return (rtexture_t *)glt; // exact match, use existing
960                 }
961                 Con_Printf("R_LoadTexture: cache mismatch, replacing old texture\n");
962                 R_FreeTexture((rtexture_t *)glt);
963         }
964
965         return (rtexture_t *)R_SetupTexture(pool, identifier, 0, width, height, flags | GLTEXF_PROCEDURAL | GLTEXF_UPLOAD, texinfo, NULL, generate, proceduraldata, proceduraldatasize);
966 }
967
968 int R_TextureHasAlpha(rtexture_t *rt)
969 {
970         gltexture_t *glt;
971         if (!rt)
972                 return false;
973         glt = (gltexture_t *)rt;
974         return (glt->flags & TEXF_ALPHA) != 0;
975 }
976
977 int R_TextureWidth(rtexture_t *rt)
978 {
979         if (!rt)
980                 return false;
981         return ((gltexture_t *)rt)->width;
982 }
983
984 int R_TextureHeight(rtexture_t *rt)
985 {
986         if (!rt)
987                 return false;
988         return ((gltexture_t *)rt)->height;
989 }
990
991 void R_FragmentLocation(rtexture_t *rt, int *x, int *y, float *fx1, float *fy1, float *fx2, float *fy2)
992 {
993         gltexture_t *glt;
994         float iwidth, iheight;
995         if (cls.state == ca_dedicated)
996         {
997                 if (x)
998                         *x = 0;
999                 if (y)
1000                         *y = 0;
1001                 if (fx1 || fy1 || fx2 || fy2)
1002                 {
1003                         if (fx1)
1004                                 *fx1 = 0;
1005                         if (fy1)
1006                                 *fy1 = 0;
1007                         if (fx2)
1008                                 *fx2 = 1;
1009                         if (fy2)
1010                                 *fy2 = 1;
1011                 }
1012                 return;
1013         }
1014         if (!rt)
1015                 Host_Error("R_FragmentLocation: no texture supplied\n");
1016         glt = (gltexture_t *)rt;
1017         if (glt->flags & TEXF_FRAGMENT)
1018         {
1019                 if (x)
1020                         *x = glt->x;
1021                 if (y)
1022                         *y = glt->y;
1023                 if (fx1 || fy1 || fx2 || fy2)
1024                 {
1025                         iwidth = 1.0f / glt->image->width;
1026                         iheight = 1.0f / glt->image->height;
1027                         if (fx1)
1028                                 *fx1 = glt->x * iwidth;
1029                         if (fy1)
1030                                 *fy1 = glt->y * iheight;
1031                         if (fx2)
1032                                 *fx2 = (glt->x + glt->width) * iwidth;
1033                         if (fy2)
1034                                 *fy2 = (glt->y + glt->height) * iheight;
1035                 }
1036         }
1037         else
1038         {
1039                 if (x)
1040                         *x = 0;
1041                 if (y)
1042                         *y = 0;
1043                 if (fx1 || fy1 || fx2 || fy2)
1044                 {
1045                         if (fx1)
1046                                 *fx1 = 0;
1047                         if (fy1)
1048                                 *fy1 = 0;
1049                         if (fx2)
1050                                 *fx2 = 1;
1051                         if (fy2)
1052                                 *fy2 = 1;
1053                 }
1054         }
1055 }
1056
1057 int R_CompatibleFragmentWidth(int width, int textype, int flags)
1058 {
1059         textypeinfo_t *texinfo = R_GetTexTypeInfo(textype, flags);
1060         while ((width * texinfo->internalbytesperpixel) & 3)
1061                 width++;
1062         return width;
1063 }
1064
1065 void R_UpdateTexture(rtexture_t *rt, qbyte *data)
1066 {
1067         gltexture_t *glt;
1068         if (rt == NULL)
1069                 Host_Error("R_UpdateTexture: no texture supplied\n");
1070         if (data == NULL)
1071                 Host_Error("R_UpdateTexture: no data supplied\n");
1072         glt = (gltexture_t *)rt;
1073
1074         // if it has not been uploaded yet, update the data that will be used when it is
1075         if (glt->inputtexels)
1076                 memcpy(glt->inputtexels, data, glt->width * glt->height * glt->textype->inputbytesperpixel);
1077         else
1078                 R_Upload(glt, data);
1079 }
1080