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