]> icculus.org git repositories - taylor/freespace2.git/blob - src/graphics/grgl2texture.cpp
fix crash in tcache_flush() when tcache hasn't been initted yet
[taylor/freespace2.git] / src / graphics / grgl2texture.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell
5  * or otherwise commercially exploit the source or things you created based on
6  * the source.
7  */
8
9 #include "SDL_opengles2.h"
10
11 #include "pstypes.h"
12 #include "2d.h"
13 #include "gropengl.h"
14 #include "gropenglinternal.h"
15 #include "grgl2.h"
16 #include "bmpman.h"
17 #include "grinternal.h"
18 #include "systemvars.h"
19 #include "osregistry.h"
20
21
22 static bool vram_full = false;
23
24 struct tcache_slot_opengl2 {
25         GLuint texture_handle;
26         int bitmap_id;
27         int size;
28         int used_this_frame;
29         int time_created;
30         int is_mipmaped;
31         ushort w;
32         ushort h;
33
34         gr_texture_source texture_mode;
35 };
36
37 static tcache_slot_opengl2 *Textures = NULL;
38
39 static tcache_slot_opengl2 *GL_bound_texture;
40
41 static int GL_frame_count = 0;
42 static int GL_last_bitmap_id = -1;
43 static int GL_last_detail = -1;
44 static int GL_last_bitmap_type = -1;
45 static int GL_should_preload = 0;
46
47 static ubyte GL_xlat[256] = { 0 };
48
49 extern int Gr_textures_in;
50 extern int bm_get_cache_slot( int bitmap_id, int separate_ani_frames );
51
52 extern bool Use_mipmaps;
53
54
55 void opengl2_set_texture_state(gr_texture_source ts)
56 {
57         if (ts == TEXTURE_SOURCE_NONE) {
58                 GL_bound_texture = NULL;
59
60                 glBindTexture(GL_TEXTURE_2D, 0);
61                 opengl2_tcache_set(-1, -1);
62         } else if (GL_bound_texture && GL_bound_texture->texture_mode != ts) {
63                 switch (ts) {
64                         case TEXTURE_SOURCE_DECAL:
65                                 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
66                                 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
67                                 break;
68
69                         case TEXTURE_SOURCE_NO_FILTERING: {
70                                 if (GL_bound_texture->is_mipmaped) {
71                                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
72                                 } else {
73                                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
74                                 }
75
76                                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
77
78                                 break;
79                         }
80
81                         default:
82                                 break;
83                 }
84
85                 GL_bound_texture->texture_mode = ts;
86         }
87 }
88
89 void opengl2_tcache_init()
90 {
91         GL_should_preload = os_config_read_uint("Video", "PreloadTextures", 1);
92
93         Textures = (tcache_slot_opengl2 *) malloc(MAX_BITMAPS * sizeof(tcache_slot_opengl2));
94
95         if (Textures == NULL) {
96                 exit(1);
97         }
98
99         SDL_assert(gr_screen.use_sections == 0);
100
101         SDL_zerop(Textures);
102
103         for (int i = 0; i < MAX_BITMAPS; i++) {
104                 Textures[i].bitmap_id = -1;
105         }
106
107         GL_last_detail = Detail.hardware_textures;
108         GL_last_bitmap_id = -1;
109         GL_last_bitmap_type     = -1;
110
111         SDL_zero(GL_xlat);
112 }
113
114 static int opengl2_free_texture(tcache_slot_opengl2 *t)
115 {
116         if (t->bitmap_id < 0) {
117                 return 1;
118         }
119
120         // if I have been used this frame, bail
121         if (t->used_this_frame == GL_frame_count) {
122                 return 0;
123         }
124
125         glDeleteTextures(1, &t->texture_handle);
126
127         if (GL_last_bitmap_id == t->bitmap_id) {
128                 GL_last_bitmap_id = -1;
129         }
130
131         Gr_textures_in -= t->size;
132
133         t->texture_handle = 0;
134         t->bitmap_id = -1;
135         t->used_this_frame = 0;
136         t->size = 0;
137         t->is_mipmaped = 0;
138
139         return 1;
140 }
141
142 void opengl2_tcache_flush()
143 {
144         if (Textures == NULL) {
145                 return;
146         }
147
148         for (int i = 0; i < MAX_BITMAPS; i++) {
149                 opengl2_free_texture(&Textures[i]);
150         }
151
152         if (Gr_textures_in != 0) {
153                 mprintf(("WARNING: VRAM is at %d instead of zero after flushing!\n", Gr_textures_in));
154                 Gr_textures_in = 0;
155         }
156
157         GL_last_bitmap_id = -1;
158
159         vram_full = false;
160 }
161
162 void opengl2_tcache_cleanup()
163 {
164         opengl2_tcache_flush();
165
166         if (Textures) {
167                 free(Textures);
168                 Textures = NULL;
169         }
170 }
171
172 void opengl2_tcache_frame()
173 {
174         GL_last_bitmap_id = -1;
175         GL_frame_count++;
176
177         if (vram_full) {
178                 opengl2_tcache_flush();
179                 vram_full = false;
180         }
181 }
182
183 static int opengl2_create_texture_sub(int bitmap_handle, int bitmap_type, bitmap *bmp, tcache_slot_opengl2 *t, int tex_w, int tex_h, bool reload, bool resize, int fail_on_full)
184 {
185         // bogus
186         if ( (bmp == NULL) || (t == NULL) ) {
187                 return 0;
188         }
189
190         if (t->used_this_frame == GL_frame_count) {
191                 mprintf(("ARGHH!!! Texture already used this frame! Cannot free it!\n"));
192                 return 0;
193         }
194
195         if ( !reload ) {
196                 if ( !opengl2_free_texture(t) ) {
197                         return 0;
198                 }
199
200                 glGenTextures(1, &t->texture_handle);
201
202                 if ( !t->texture_handle ) {
203                         nprintf(("Error", "!!DEBUG!! t->texture_handle == 0"));
204                         return 0;
205                 }
206         }
207
208         t->texture_mode = TEXTURE_SOURCE_NO_FILTERING;
209
210         glBindTexture(GL_TEXTURE_2D, t->texture_handle);
211
212         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
213         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
214
215         glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
216         glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
217
218         ubyte *bmp_data = (ubyte*)bmp->data;
219         ubyte *texmem = NULL, *texmemp;
220         int i, j;
221         int size = 0;
222
223         switch (bitmap_type) {
224                 case TCACHE_TYPE_AABITMAP: {
225                         SDL_assert(tex_w == bmp->w);
226                         SDL_assert(tex_h == bmp->h);
227
228                         texmem = (ubyte *) malloc(tex_w * tex_h);
229                         texmemp = texmem;
230
231                         for (i = 0; i < tex_h; i++) {
232                                 for (j = 0; j < tex_w; j++) {
233                                         *texmemp++ = GL_xlat[bmp_data[i*bmp->w+j]];
234                                 }
235                         }
236
237                         size = tex_w * tex_h;
238
239                         if (reload) {
240                                 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, tex_w, tex_h, GL_ALPHA, GL_UNSIGNED_BYTE, texmem);
241                         } else {
242                                 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, tex_w, tex_h, 0, GL_ALPHA, GL_UNSIGNED_BYTE, texmem);
243                         }
244
245                         free(texmem);
246
247                         break;
248                 }
249
250                 case TCACHE_TYPE_BITMAP_INTERFACE:
251                 case TCACHE_TYPE_BITMAP_SECTION: {
252                         size = tex_w * tex_h * 2;
253
254                         if (reload) {
255                                 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, tex_w, tex_h, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, bmp_data);
256                         } else {
257                                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_w, tex_h, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, bmp_data);
258                         }
259
260                         break;
261                 }
262
263                 default: {
264                         if (resize) {
265                                 texmem = (ubyte *) malloc(tex_w * tex_h * 2);
266                                 texmemp = texmem;
267
268                                 SDL_assert(texmem);
269
270                                 fix u = 0, utmp, v = 0, du, dv;
271
272                                 du = ((bmp->w - 1) * F1_0) / tex_w;
273                                 dv = ((bmp->h - 1) * F1_0) / tex_h;
274
275                                 for (j = 0; j < tex_h; j++) {
276                                         utmp = u;
277
278                                         for (i = 0; i < tex_w; i++) {
279                                                 *texmemp++ = bmp_data[(f2i(v)*bmp->w+f2i(utmp))*2+0];
280                                                 *texmemp++ = bmp_data[(f2i(v)*bmp->w+f2i(utmp))*2+1];
281
282                                                 utmp += du;
283                                         }
284
285                                         v += dv;
286                                 }
287                         }
288
289                         if (reload) {
290                                 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, tex_w, tex_h, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, (resize) ? texmem : bmp_data);
291                         } else {
292                                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_w, tex_h, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, (resize) ? texmem : bmp_data);
293                         }
294
295                         if (texmem) {
296                                 free(texmem);
297                         }
298
299                         if (Use_mipmaps) {
300                                 glGenerateMipmap(GL_TEXTURE_2D);
301
302                                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
303                                 t->is_mipmaped = 1;
304
305                                 size = fl2i(tex_w * tex_h * 2.0f * 1.3333333f);
306                         } else {
307                                 size = tex_w * tex_h * 2;
308                         }
309
310                         break;
311                 }
312         }
313
314         t->bitmap_id = bitmap_handle;
315         t->time_created = GL_frame_count;
316         t->used_this_frame = 0;
317         t->size = size;
318         t->w = (ushort)tex_w;
319         t->h = (ushort)tex_h;
320
321         if ( !reload ) {
322                 Gr_textures_in += t->size;
323         }
324
325         return 1;
326 }
327
328 static int opengl2_create_texture(int bitmap_handle, int bitmap_type, tcache_slot_opengl2 *tslot, int fail_on_full)
329 {
330         ubyte flags = 0;
331         bool cull_size = false;
332         bool resize = false;
333         ubyte bpp = 16;
334
335         switch (bitmap_type) {
336                 case TCACHE_TYPE_AABITMAP: {
337                         flags |= BMP_AABITMAP;
338                         bpp = 8;
339
340                         break;
341                 }
342
343                 case TCACHE_TYPE_NORMAL: {
344                         flags |= BMP_TEX_OTHER;
345                         cull_size = true;
346
347                         break;
348                 }
349
350                 case TCACHE_TYPE_BITMAP_INTERFACE:
351                 case TCACHE_TYPE_XPARENT: {
352                         flags |= BMP_TEX_XPARENT;
353
354                         break;
355                 }
356
357                 default:
358                         Int3();
359                         return 0;
360         }
361
362         bitmap *bmp = bm_lock(bitmap_handle, bpp, flags);
363
364         if (bmp == NULL) {
365                 mprintf(("Couldn't lock bitmap %d\n", bitmap_handle));
366                 return 0;
367         }
368
369         int max_w = bmp->w;
370         int max_h = bmp->h;
371
372         if ( cull_size && (Detail.hardware_textures < 4) ) {
373                 // if we are going to cull the size then we need to force a resize
374                 resize = true;
375
376                 // Detail.hardware_textures goes form 0 to 4
377                 int val = 16 >> Detail.hardware_textures;
378
379                 max_w /= val;
380                 max_h /= val;
381         }
382
383         if ( (max_w < 1) || (max_h < 1) ) {
384                 mprintf(("Bitmap %d is too small at %dx%d\n", bitmap_handle, max_w, max_h));
385                 return 0;
386         }
387
388         bool reload = false;
389
390         // see if we can reuse this slot for a new bitmap
391         if ( tslot->texture_handle && (tslot->bitmap_id != bitmap_handle) ) {
392                 if ( (max_w == tslot->w) && (max_h == tslot->h) ) {
393                         reload = true;
394                 }
395         }
396
397         // call the helper
398         int ret_val = opengl2_create_texture_sub(bitmap_handle, bitmap_type, bmp, tslot, max_w, max_h, reload, resize, fail_on_full);
399
400         // unlock the bitmap
401         bm_unlock(bitmap_handle);
402
403         return ret_val;
404 }
405
406 int opengl2_tcache_set(int bitmap_id, int bitmap_type, int fail_on_full)
407 {
408         if (bitmap_id < 0) {
409                 GL_last_bitmap_id = -1;
410
411                 return 0;
412         }
413
414         if (GL_last_detail != Detail.hardware_textures) {
415                 opengl2_tcache_flush();
416                 GL_last_detail = Detail.hardware_textures;
417         }
418
419         if (vram_full) {
420                 return 0;
421         }
422
423         int n = bm_get_cache_slot(bitmap_id, 1);
424         tcache_slot_opengl2 *t = &Textures[n];
425
426         if ( (GL_last_bitmap_id == bitmap_id) && (GL_last_bitmap_type == bitmap_type) && (t->bitmap_id == bitmap_id) ) {
427                 t->used_this_frame = GL_frame_count;
428
429                 return 1;
430         }
431
432         int ret_val = 1;
433
434         if (bitmap_id != t->bitmap_id) {
435                 ret_val = opengl2_create_texture(bitmap_id, bitmap_type, t, fail_on_full);
436         }
437
438         if (ret_val && t->texture_handle && !vram_full) {
439                 glBindTexture(GL_TEXTURE_2D, t->texture_handle);
440
441                 GL_bound_texture = t;
442
443                 GL_last_bitmap_id = t->bitmap_id;
444                 GL_last_bitmap_type = bitmap_type;
445
446                 t->used_this_frame = GL_frame_count;
447         } else {
448                 GL_last_bitmap_id = -1;
449                 GL_last_bitmap_type = -1;
450
451                 GL_bound_texture = NULL;
452
453                 glBindTexture(GL_TEXTURE_2D, 0);
454
455                 return 0;
456         }
457
458         return 1;
459 }
460
461 void gr_opengl2_preload_init()
462 {
463         opengl2_tcache_flush();
464 }
465
466 int gr_opengl2_preload(int bitmap_num, int is_aabitmap)
467 {
468         if ( !GL_should_preload ) {
469                 return 0;
470         }
471
472         int bitmap_type = (is_aabitmap) ? TCACHE_TYPE_AABITMAP : TCACHE_TYPE_NORMAL;
473
474         int retval = opengl2_tcache_set(bitmap_num, bitmap_type, 1);
475
476         if ( !retval ) {
477                 mprintf(("Texture upload failed bit bitmap %d!\n", bitmap_num));
478         }
479
480         return retval;
481 }
482
483 void gr_opengl2_set_gamma(float)
484 {
485         // set the alpha gamma settings (for fonts)
486         for (int i = 0; i < 16; i++) {
487                 GL_xlat[i] = (ubyte)Gr_gamma_lookup[(i*255)/15];
488         }
489
490         GL_xlat[15] = GL_xlat[1];
491
492         // Flush any existing textures
493         opengl2_tcache_flush();
494 }
495
496 void gr_opengl2_release_texture(int handle)
497 {
498         for (int i = 0; i < MAX_BITMAPS; i++) {
499                 tcache_slot_opengl2 *t = &Textures[i];
500
501                 if (t->bitmap_id == handle) {
502                         t->used_this_frame = 0;
503                         opengl2_free_texture(t);
504
505                         break;
506                 }
507         }
508 }