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