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