2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
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
11 #include "SDL_opengl.h"
16 #include "gropenglinternal.h"
19 #include "grinternal.h"
20 #include "systemvars.h"
21 #include "osregistry.h"
24 static int vram_full = 0;
26 typedef struct tcache_slot_opengl {
27 GLuint texture_handle;
28 float u_scale, v_scale;
36 tcache_slot_opengl *data_sections[MAX_BMAP_SECTIONS_X][MAX_BMAP_SECTIONS_Y];
37 tcache_slot_opengl *parent;
39 gr_texture_source texture_mode;
42 static void *Texture_sections = NULL;
43 static tcache_slot_opengl *Textures = NULL;
45 static tcache_slot_opengl *GL_bound_texture;
47 static int GL_frame_count = 0;
48 static int GL_last_bitmap_id = -1;
49 static int GL_last_detail = -1;
50 static int GL_last_bitmap_type = -1;
51 static int GL_last_section_x = -1;
52 static int GL_last_section_y = -1;
53 static int GL_should_preload = 0;
55 extern int Gr_textures_in;
57 static gr_texture_source GL_current_texture_source = (gr_texture_source) -1;
59 static ubyte GL_xlat[256] = { 0 };
61 extern int bm_get_cache_slot( int bitmap_id, int separate_ani_frames );
64 void opengl1_set_texture_state(gr_texture_source ts)
66 if (ts == TEXTURE_SOURCE_NONE) {
67 GL_bound_texture = NULL;
69 glBindTexture(GL_TEXTURE_2D, 0);
70 opengl1_tcache_set(-1, -1, NULL, NULL, 0, -1, -1, 0 );
71 } else if (GL_bound_texture &&
72 GL_bound_texture->texture_mode != ts) {
74 case TEXTURE_SOURCE_DECAL:
75 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
76 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
78 case TEXTURE_SOURCE_NO_FILTERING:
79 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
80 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
86 GL_bound_texture->texture_mode = ts;
89 GL_current_texture_source = ts;
93 void opengl1_tcache_init()
97 if ( os_config_read_uint("Video", "PreloadTextures", 1) ) {
98 GL_should_preload = 1;
100 GL_should_preload = 0;
103 Textures = (tcache_slot_opengl *)malloc(MAX_BITMAPS*sizeof(tcache_slot_opengl));
108 if (gr_screen.use_sections) {
109 Texture_sections = (tcache_slot_opengl*)malloc(MAX_BITMAPS * MAX_BMAP_SECTIONS_X * MAX_BMAP_SECTIONS_Y * sizeof(tcache_slot_opengl));
110 if(!Texture_sections){
113 memset(Texture_sections, 0, MAX_BITMAPS * MAX_BMAP_SECTIONS_X * MAX_BMAP_SECTIONS_Y * sizeof(tcache_slot_opengl));
116 // Init the texture structures
117 int section_count = 0;
118 for( i=0; i<MAX_BITMAPS; i++ ) {
119 Textures[i].texture_handle = 0;
121 Textures[i].bitmap_id = -1;
122 Textures[i].size = 0;
123 Textures[i].used_this_frame = 0;
125 Textures[i].parent = NULL;
128 if (gr_screen.use_sections) {
129 for(idx=0; idx<MAX_BMAP_SECTIONS_X; idx++){
130 for(s_idx=0; s_idx<MAX_BMAP_SECTIONS_Y; s_idx++){
131 Textures[i].data_sections[idx][s_idx] = &((tcache_slot_opengl*)Texture_sections)[section_count++];
132 Textures[i].data_sections[idx][s_idx]->parent = &Textures[i];
133 Textures[i].data_sections[idx][s_idx]->texture_handle = 0;
134 Textures[i].data_sections[idx][s_idx]->bitmap_id = -1;
135 Textures[i].data_sections[idx][s_idx]->size = 0;
136 Textures[i].data_sections[idx][s_idx]->used_this_frame = 0;
140 for(idx=0; idx<MAX_BMAP_SECTIONS_X; idx++){
141 for(s_idx=0; s_idx<MAX_BMAP_SECTIONS_Y; s_idx++){
142 Textures[i].data_sections[idx][s_idx] = NULL;
148 GL_last_detail = Detail.hardware_textures;
149 GL_last_bitmap_id = -1;
150 GL_last_bitmap_type = -1;
152 GL_last_section_x = -1;
153 GL_last_section_y = -1;
155 memset(GL_xlat, 0, sizeof(GL_xlat));
158 static int opengl1_free_texture ( tcache_slot_opengl *t )
164 if ( t->bitmap_id > -1 ) {
165 // if I, or any of my children have been used this frame, bail
166 if(t->used_this_frame == GL_frame_count){
170 if (gr_screen.use_sections) {
171 for(idx=0; idx<MAX_BMAP_SECTIONS_X; idx++){
172 for(s_idx=0; s_idx<MAX_BMAP_SECTIONS_Y; s_idx++){
173 if((t->data_sections[idx][s_idx] != NULL) && (t->data_sections[idx][s_idx]->used_this_frame == GL_frame_count)){
180 // ok, now we know its legal to free everything safely
181 t->texture_mode = (gr_texture_source) -1;
182 glDeleteTextures (1, &t->texture_handle);
183 t->texture_handle = 0;
185 if ( GL_last_bitmap_id == t->bitmap_id ) {
186 GL_last_bitmap_id = -1;
189 // if this guy has children, free them too, since the children
190 // actually make up his size
191 if (gr_screen.use_sections) {
192 for(idx=0; idx<MAX_BMAP_SECTIONS_X; idx++){
193 for(s_idx=0; s_idx<MAX_BMAP_SECTIONS_Y; s_idx++){
194 if(t->data_sections[idx][s_idx] != NULL){
195 opengl1_free_texture(t->data_sections[idx][s_idx]);
202 t->used_this_frame = 0;
203 Gr_textures_in -= t->size;
210 void opengl1_tcache_flush()
214 if (Textures == NULL) {
218 for( i=0; i<MAX_BITMAPS; i++ ) {
219 opengl1_free_texture ( &Textures[i] );
221 if (Gr_textures_in != 0) {
222 mprintf(( "WARNING: VRAM is at %d instead of zero after flushing!\n", Gr_textures_in ));
226 GL_last_bitmap_id = -1;
227 GL_last_section_x = -1;
228 GL_last_section_y = -1;
231 void opengl1_tcache_cleanup()
233 opengl1_tcache_flush ();
240 if( Texture_sections != NULL ){
241 free(Texture_sections);
242 Texture_sections = NULL;
246 void opengl1_tcache_frame()
248 GL_last_bitmap_id = -1;
255 for( i=0; i<MAX_BITMAPS; i++ ) {
256 Textures[i].used_this_frame = 0;
259 if(Textures[i].data_sections[0][0] != NULL){
260 SDL_assert(GL_texture_sections);
261 if(GL_texture_sections){
262 for(idx=0; idx<MAX_BMAP_SECTIONS_X; idx++){
263 for(s_idx=0; s_idx<MAX_BMAP_SECTIONS_Y; s_idx++){
264 if(Textures[i].data_sections[idx][s_idx] != NULL){
265 Textures[i].data_sections[idx][s_idx]->used_this_frame = 0;
275 opengl1_tcache_flush();
280 static void opengl1_tcache_get_adjusted_texture_size(int w_in, int h_in, int *w_out, int *h_out)
286 if((w_out == NULL) || (h_out == NULL)){
294 // set height and width to a power of 2
295 for (i=0; i<16; i++ ) {
296 if ( (tex_w > (1<<i)) && (tex_w <= (1<<(i+1))) ) {
302 for (i=0; i<16; i++ ) {
303 if ( (tex_h > (1<<i)) && (tex_h <= (1<<(i+1))) ) {
309 // try to keep an 8:1 size ratio
315 if ( tex_w < GL_min_texture_width ) {
316 tex_w = GL_min_texture_width;
317 } else if ( tex_w > GL_max_texture_width ) {
318 tex_w = GL_max_texture_width;
321 if ( tex_h < GL_min_texture_height ) {
322 tex_h = GL_min_texture_height;
323 } else if ( tex_h > GL_max_texture_height ) {
324 tex_h = GL_max_texture_height;
327 // store the outgoing size
332 // bmp == bitmap structure with w, h, and data
333 // sx == x offset into bitmap
334 // sy == y offset into bitmap
335 // src_w == absolute width of section on source bitmap
336 // src_h == absolute height of section on source bitmap
337 // bmap_w == width of source bitmap
338 // bmap_h == height of source bitmap
339 // tex_w == width of final texture
340 // tex_h == height of final texture
341 static int opengl1_create_texture_sub(int bitmap_handle, int bitmap_type, bitmap *bmp, tcache_slot_opengl *t, int sx, int sy, int src_w, int src_h, int tex_w, int tex_h, bool reload, bool resize, int fail_on_full)
344 if ( (bmp == NULL) || (t == NULL) ) {
348 if (t->used_this_frame == GL_frame_count) {
349 mprintf(("ARGHH!!! Texture already used this frame! Cannot free it!\n"));
354 if ( !opengl1_free_texture(t) ) {
358 glGenTextures(1, &t->texture_handle);
360 if ( !t->texture_handle ) {
361 nprintf(("Error", "!!DEBUG!! t->texture_handle == 0"));
366 switch (bitmap_type) {
367 case TCACHE_TYPE_AABITMAP:
368 t->u_scale = (float)bmp->w / (float)tex_w;
369 t->v_scale = (float)bmp->h / (float)tex_h;
372 case TCACHE_TYPE_BITMAP_INTERFACE:
373 case TCACHE_TYPE_BITMAP_SECTION:
374 t->u_scale = (float)src_w / (float)tex_w;
375 t->v_scale = (float)src_h / (float)tex_h;
384 t->texture_mode = TEXTURE_SOURCE_NO_FILTERING;
386 glBindTexture(GL_TEXTURE_2D, t->texture_handle);
388 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
389 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
391 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
392 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
394 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
396 ubyte *bmp_data = (ubyte*)bmp->data;
397 ubyte *texmem = NULL, *texmemp;
401 switch (bitmap_type) {
402 case TCACHE_TYPE_AABITMAP: {
403 texmem = (ubyte *) malloc(tex_w * tex_h);
406 for (i = 0; i < tex_h; i++) {
407 for (j = 0;j < tex_w; j++) {
408 if ( (i < bmp->h) && (j < bmp->w) ) {
409 *texmemp++ = GL_xlat[bmp_data[i*bmp->w+j]];
416 size = tex_w * tex_h;
419 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, tex_w, tex_h, GL_ALPHA, GL_UNSIGNED_BYTE, texmem);
421 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, tex_w, tex_h, 0, GL_ALPHA, GL_UNSIGNED_BYTE, texmem);
429 case TCACHE_TYPE_BITMAP_INTERFACE:
430 case TCACHE_TYPE_BITMAP_SECTION: {
431 // if we aren't resizing in any way then we can just use bmp_data directly
433 texmem = (ubyte *) malloc(tex_w * tex_h * 2);
436 for (i = 0;i < tex_h; i++) {
437 for (j = 0; j < tex_w; j++) {
438 if ( (i < src_h) && (j < src_w) ) {
439 *texmemp++ = bmp_data[((i+sy)*bmp->w+(j+sx))*2+0];
440 *texmemp++ = bmp_data[((i+sy)*bmp->w+(j+sx))*2+1];
449 size = tex_w * tex_h * 2;
452 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, tex_w, tex_h, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, (resize) ? texmem : bmp_data);
454 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);
465 // if we aren't resizing then we can just use bmp_data directly
467 texmem = (ubyte *) malloc (tex_w * tex_h * 2);
472 fix u = 0, utmp, v = 0, du, dv;
474 du = ((bmp->w - 1) * F1_0) / tex_w;
475 dv = ((bmp->h - 1) * F1_0) / tex_h;
477 for (j = 0; j < tex_h; j++) {
480 for (i = 0; i < tex_w; i++) {
481 *texmemp++ = bmp_data[(f2i(v)*bmp->w+f2i(utmp))*2+0];
482 *texmemp++ = bmp_data[(f2i(v)*bmp->w+f2i(utmp))*2+1];
491 size = tex_w * tex_h * 2;
494 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, tex_w, tex_h, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, (resize) ? texmem : bmp_data);
496 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);
507 t->bitmap_id = bitmap_handle;
508 t->time_created = GL_frame_count;
509 t->used_this_frame = 0;
511 t->w = (ushort)tex_w;
512 t->h = (ushort)tex_h;
515 Gr_textures_in += t->size;
521 static int opengl1_create_texture(int bitmap_handle, int bitmap_type, tcache_slot_opengl *tslot, int fail_on_full)
524 int final_w, final_h;
527 bool cull_size = false;
529 // setup texture/bitmap flags
531 case TCACHE_TYPE_AABITMAP:
532 flags |= BMP_AABITMAP;
535 case TCACHE_TYPE_NORMAL:
536 flags |= BMP_TEX_OTHER;
539 case TCACHE_TYPE_BITMAP_INTERFACE:
540 case TCACHE_TYPE_XPARENT:
541 flags |= BMP_TEX_XPARENT;
548 // lock the bitmap into the proper format
549 bitmap *bmp = bm_lock(bitmap_handle, bpp, flags);
551 mprintf(("Couldn't lock bitmap %d.\n", bitmap_handle ));
558 if ( cull_size && (Detail.hardware_textures < 4) ) {
559 // if we are going to cull the size then we need to force a resize
562 // Detail.hardware_textures goes form 0 to 4
563 int val = 16 >> Detail.hardware_textures;
569 // get final texture size as it will be allocated as a DD surface
570 opengl1_tcache_get_adjusted_texture_size(max_w, max_h, &final_w, &final_h);
572 if ( (final_w < 1) || (final_h < 1) ) {
573 mprintf(("Bitmap is too small at %dx%d.\n", final_w, final_h));
577 // if we don't have to resize the image (to get power of 2, etc.) then skip that extra work
578 if ( (max_w != final_w) || (max_h != final_h) ) {
584 // see if we can reuse this slot for a new bitmap
585 if ( tslot->texture_handle && (tslot->bitmap_id != bitmap_handle) ) {
586 if ( (final_w == tslot->w) && (final_h == tslot->h) ) {
592 int ret_val = opengl1_create_texture_sub(bitmap_handle, bitmap_type, bmp, tslot, 0, 0, bmp->w, bmp->h, final_w, final_h, reload, resize, fail_on_full);
595 bm_unlock(bitmap_handle);
600 static int opengl1_create_texture_sectioned(int bitmap_handle, int bitmap_type, tcache_slot_opengl *tslot, int sx, int sy, int fail_on_full)
602 int final_w, final_h;
603 int section_x, section_y;
606 SDL_assert( gr_screen.use_sections );
608 // setup texture/bitmap flags
609 SDL_assert(bitmap_type == TCACHE_TYPE_BITMAP_SECTION);
610 if(bitmap_type != TCACHE_TYPE_BITMAP_SECTION){
611 bitmap_type = TCACHE_TYPE_BITMAP_SECTION;
614 // lock the bitmap in the proper format
615 bitmap *bmp = bm_lock(bitmap_handle, 16, BMP_TEX_XPARENT);
617 mprintf(("Couldn't lock bitmap %d.\n", bitmap_handle ));
620 // determine the width and height of this section
621 bm_get_section_size(bitmap_handle, sx, sy, §ion_x, §ion_y);
623 // get final texture size as it will be allocated as an opengl texture
624 opengl1_tcache_get_adjusted_texture_size(section_x, section_y, &final_w, &final_h);
626 if ( (final_w < 1) || (final_h < 1) ) {
627 mprintf(("Bitmap is too small at %dx%d.\n", final_w, final_h));
631 // if we don't have to resize the image (to get power of 2, etc.) then skip that extra work
632 if ( (bmp->sections.num_x == 1) && (bmp->sections.num_y == 1) && (section_x == final_w) && (section_y == final_h) ) {
638 // see if we can reuse this slot for a new bitmap
639 if ( tslot->texture_handle && (tslot->bitmap_id != bitmap_handle) ) {
640 if ( (final_w == tslot->w) && (final_h == tslot->h) ) {
646 int ret_val = opengl1_create_texture_sub(bitmap_handle, bitmap_type, bmp, tslot, bmp->sections.sx[sx], bmp->sections.sy[sy], section_x, section_y, final_w, final_h, reload, resize, fail_on_full);
649 bm_unlock(bitmap_handle);
654 int opengl1_tcache_set(int bitmap_id, int bitmap_type, float *u_scale, float *v_scale, int fail_on_full, int sx, int sy, int force)
663 GL_last_bitmap_id = -1;
667 if ( GL_last_detail != Detail.hardware_textures ) {
668 GL_last_detail = Detail.hardware_textures;
669 opengl1_tcache_flush();
676 int n = bm_get_cache_slot (bitmap_id, 1);
677 tcache_slot_opengl *t = &Textures[n];
679 if ( (GL_last_bitmap_id == bitmap_id) && (GL_last_bitmap_type==bitmap_type) && (t->bitmap_id == bitmap_id) && (GL_last_section_x == sx) && (GL_last_section_y == sy)) {
680 t->used_this_frame = GL_frame_count;
682 // mark all children as used
683 if (gr_screen.use_sections) {
684 for(idx=0; idx<MAX_BMAP_SECTIONS_X; idx++){
685 for(s_idx=0; s_idx<MAX_BMAP_SECTIONS_Y; s_idx++){
686 if(t->data_sections[idx][s_idx] != NULL){
687 t->data_sections[idx][s_idx]->used_this_frame = GL_frame_count;
693 *u_scale = t->u_scale;
694 *v_scale = t->v_scale;
698 if (bitmap_type == TCACHE_TYPE_BITMAP_SECTION){
699 SDL_assert( gr_screen.use_sections );
700 SDL_assert((sx >= 0) && (sy >= 0) && (sx < MAX_BMAP_SECTIONS_X) && (sy < MAX_BMAP_SECTIONS_Y));
701 if(!((sx >= 0) && (sy >= 0) && (sx < MAX_BMAP_SECTIONS_X) && (sy < MAX_BMAP_SECTIONS_Y))){
707 // if the texture sections haven't been created yet
708 if((t->bitmap_id < 0) || (t->bitmap_id != bitmap_id)){
710 // lock the bitmap in the proper format
711 bmp = bm_lock(bitmap_id, 16, BMP_TEX_XPARENT);
712 bm_unlock(bitmap_id);
714 // now lets do something for each texture
716 for(idx=0; idx<bmp->sections.num_x; idx++){
717 for(s_idx=0; s_idx<bmp->sections.num_y; s_idx++){
718 // hmm. i'd rather we didn't have to do it this way...
719 if(!opengl1_create_texture_sectioned(bitmap_id, bitmap_type, t->data_sections[idx][s_idx], idx, s_idx, fail_on_full)){
723 // not used this frame
724 t->data_sections[idx][s_idx]->used_this_frame = 0;
728 // zero out pretty much everything in the parent struct since he's just the root
729 t->bitmap_id = bitmap_id;
730 t->texture_handle = 0;
731 t->time_created = t->data_sections[sx][sy]->time_created;
732 t->used_this_frame = 0;
735 // argh. we failed to upload. free anything we can
737 opengl1_free_texture(t);
739 // swap in the texture we want
741 t = t->data_sections[sx][sy];
744 // all other "normal" textures
745 else if((bitmap_id < 0) || (bitmap_id != t->bitmap_id)){
746 ret_val = opengl1_create_texture( bitmap_id, bitmap_type, t, fail_on_full );
749 // everything went ok
750 if(ret_val && (t->texture_handle) && !vram_full){
751 *u_scale = t->u_scale;
752 *v_scale = t->v_scale;
754 GL_bound_texture = t;
756 glBindTexture (GL_TEXTURE_2D, t->texture_handle );
758 GL_last_bitmap_id = t->bitmap_id;
759 GL_last_bitmap_type = bitmap_type;
760 GL_last_section_x = sx;
761 GL_last_section_y = sy;
763 t->used_this_frame = GL_frame_count;
767 GL_last_bitmap_id = -1;
768 GL_last_bitmap_type = -1;
770 GL_last_section_x = -1;
771 GL_last_section_y = -1;
773 GL_bound_texture = NULL;
775 glBindTexture (GL_TEXTURE_2D, 0); // test - DDOI
782 void gr_opengl1_preload_init()
784 opengl1_tcache_flush();
787 int gr_opengl1_preload(int bitmap_num, int is_aabitmap)
789 if ( !GL_should_preload ) {
793 float u_scale, v_scale;
795 int bitmap_type = TCACHE_TYPE_NORMAL;
798 bitmap_type = TCACHE_TYPE_AABITMAP;
801 retval = opengl1_tcache_set(bitmap_num, bitmap_type, &u_scale, &v_scale, 1, -1, -1, 0 );
804 mprintf(("Texture upload failed!\n" ));
810 void gr_opengl1_set_gamma(float)
814 // set the alpha gamma settings (for fonts)
815 for (i = 0; i < 16; i++) {
816 GL_xlat[i] = (ubyte)Gr_gamma_lookup[(i*255)/15];
819 GL_xlat[15] = GL_xlat[1];
821 // Flush any existing textures
822 opengl1_tcache_flush();
825 void gr_opengl1_release_texture(int handle)
827 for(int i=0; i<MAX_BITMAPS; i++ ) {
828 if (Textures[i].bitmap_id == handle) {
829 Textures[i].used_this_frame = 0; // this bmp doesn't even exist any longer...
830 opengl1_free_texture( &Textures[i] );