1 /* $Id: texobj.c,v 1.21 1998/01/16 01:09:44 brianp Exp $ */ 2 3 /* 4 * Mesa 3-D graphics library 5 * Version: 2.6 6 * Copyright (C) 1995-1997 Brian Paul 7 * 8 * This library is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Library General Public 10 * License as published by the Free Software Foundation; either 11 * version 2 of the License, or (at your option) any later version. 12 * 13 * This library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Library General Public License for more details. 17 * 18 * You should have received a copy of the GNU Library General Public 19 * License along with this library; if not, write to the Free 20 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21 */ 22 23 24 /* 25 * $Log: texobj.c,v $ 26 * Revision 1.21 1998/01/16 01:09:44 brianp 27 * glGenTextures() didn't reserve the returned texture IDs 28 * 29 * Revision 1.20 1997/12/07 17:34:05 brianp 30 * added DavidB's v0.21 fxmesa driver patch 31 * 32 * Revision 1.19 1997/11/07 03:38:07 brianp 33 * added stdio.h include for SunOS 4.x 34 * 35 * Revision 1.18 1997/10/13 23:57:59 brianp 36 * added target parameter to Driver.BindTexture() 37 * 38 * Revision 1.17 1997/09/29 23:28:14 brianp 39 * updated for new device driver texture functions 40 * 41 * Revision 1.16 1997/09/27 00:14:39 brianp 42 * added GL_EXT_paletted_texture extension 43 * 44 * Revision 1.15 1997/09/23 00:58:15 brianp 45 * now using hash table for texture objects 46 * 47 * Revision 1.14 1997/08/23 18:41:40 brianp 48 * fixed bug in glBindTexture() when binding an incomplete texture image 49 * 50 * Revision 1.13 1997/08/23 17:14:44 brianp 51 * fixed bug: glBindTexture(target, 0) caused segfault 52 * 53 * Revision 1.12 1997/07/24 01:25:34 brianp 54 * changed precompiled header symbol from PCH to PC_HEADER 55 * 56 * Revision 1.11 1997/05/28 03:26:49 brianp 57 * added precompiled header (PCH) support 58 * 59 * Revision 1.10 1997/05/17 03:41:49 brianp 60 * added code to update ctx->Texture.Current in gl_BindTexture() 61 * 62 * Revision 1.9 1997/05/03 00:52:52 brianp 63 * removed a few unused variables 64 * 65 * Revision 1.8 1997/05/01 02:08:12 brianp 66 * new implementation of gl_BindTexture() 67 * 68 * Revision 1.7 1997/04/28 23:38:45 brianp 69 * added gl_test_texture_object_completeness() 70 * 71 * Revision 1.6 1997/04/14 02:03:05 brianp 72 * added MinMagThresh to texture object 73 * 74 * Revision 1.5 1997/02/09 18:52:15 brianp 75 * added GL_EXT_texture3D support 76 * 77 * Revision 1.4 1997/01/16 03:35:34 brianp 78 * added calls to device driver DeleteTexture() and BindTexture() functions 79 * 80 * Revision 1.3 1997/01/09 19:49:47 brianp 81 * added a check to switch rasterizers if needed in glBindTexture() 82 * 83 * Revision 1.2 1996/09/27 17:09:42 brianp 84 * removed a redundant return statement 85 * 86 * Revision 1.1 1996/09/13 01:38:16 brianp 87 * Initial revision 88 * 89 */ 90 91 92 #ifdef PC_HEADER 93 #include "all.h" 94 #else 95 #include <assert.h> 96 #include <stdio.h> 97 #include <stdlib.h> 98 #include "context.h" 99 #include "hash.h" 100 #include "macros.h" 101 #include "teximage.h" 102 #include "texobj.h" 103 #include "types.h" 104 #endif 105 106 107 108 /* 109 * Allocate a new texture object and add it to the linked list of texture 110 * objects. If name>0 then also insert the new texture object into the hash 111 * table. 112 * Input: shared - the shared GL state structure to contain the texture object 113 * name - integer name for the texture object 114 * dimensions - either 1, 2 or 3 115 * Return: pointer to new texture object 116 */ 117 struct gl_texture_object * 118 gl_alloc_texture_object( struct gl_shared_state *shared, GLuint name, 119 GLuint dimensions) 120 { 121 struct gl_texture_object *obj; 122 123 assert(dimensions >= 0 && dimensions <= 2); 124 125 obj = (struct gl_texture_object *) 126 calloc(1,sizeof(struct gl_texture_object)); 127 if (obj) { 128 /* init the non-zero fields */ 129 obj->Name = name; 130 obj->Dimensions = dimensions; 131 obj->WrapS = GL_REPEAT; 132 obj->WrapT = GL_REPEAT; 133 obj->MinFilter = GL_NEAREST_MIPMAP_LINEAR; 134 obj->MagFilter = GL_LINEAR; 135 obj->MinMagThresh = 0.0F; 136 obj->Palette[0] = 255; 137 obj->Palette[1] = 255; 138 obj->Palette[2] = 255; 139 obj->Palette[3] = 255; 140 obj->PaletteSize = 1; 141 obj->PaletteIntFormat = GL_RGBA; 142 obj->PaletteFormat = GL_RGBA; 143 144 /* insert into linked list */ 145 if (shared) { 146 obj->Next = shared->TexObjectList; 147 shared->TexObjectList = obj; 148 } 149 150 if (name > 0) { 151 /* insert into hash table */ 152 HashInsert(shared->TexObjects, name, obj); 153 } 154 } 155 return obj; 156 } 157 158 159 /* 160 * Deallocate a texture object struct and remove it from the given 161 * shared GL state. 162 * Input: shared - the shared GL state to which the object belongs 163 * t - the texture object to delete 164 */ 165 void gl_free_texture_object( struct gl_shared_state *shared, 166 struct gl_texture_object *t ) 167 { 168 struct gl_texture_object *tprev, *tcurr; 169 170 assert(t); 171 172 /* unlink t from the linked list */ 173 if (shared) { 174 tprev = NULL; 175 tcurr = shared->TexObjectList; 176 while (tcurr) { 177 if (tcurr==t) { 178 if (tprev) { 179 tprev->Next = t->Next; 180 } 181 else { 182 shared->TexObjectList = t->Next; 183 } 184 break; 185 } 186 tprev = tcurr; 187 tcurr = tcurr->Next; 188 } 189 } 190 191 if (t->Name) { 192 /* remove from hash table */ 193 HashRemove(shared->TexObjects, t->Name); 194 } 195 196 /* free texture image */ 197 { 198 GLuint i; 199 for (i=0;i<MAX_TEXTURE_LEVELS;i++) { 200 if (t->Image[i]) { 201 gl_free_texture_image( t->Image[i] ); 202 } 203 } 204 } 205 /* free this object */ 206 free( t ); 207 } 208 209 210 211 /* 212 * Examine a texture object to determine if it is complete or not. 213 * The t->Complete flag will be set to GL_TRUE or GL_FALSE accordingly. 214 */ 215 void gl_test_texture_object_completeness( struct gl_texture_object *t ) 216 { 217 t->Complete = GL_TRUE; /* be optimistic */ 218 219 /* Always need level zero image */ 220 if (!t->Image[0] || !t->Image[0]->Data) { 221 t->Complete = GL_FALSE; 222 return; 223 } 224 225 if (t->MinFilter!=GL_NEAREST && t->MinFilter!=GL_LINEAR) { 226 /* 227 * Mipmapping: determine if we have a complete set of mipmaps 228 */ 229 int i; 230 231 /* Test dimension-independent attributes */ 232 for (i=1; i<MAX_TEXTURE_LEVELS; i++) { 233 if (t->Image[i]) { 234 if (!t->Image[i]->Data) { 235 t->Complete = GL_FALSE; 236 return; 237 } 238 if (t->Image[i]->Format != t->Image[0]->Format) { 239 t->Complete = GL_FALSE; 240 return; 241 } 242 if (t->Image[i]->Border != t->Image[0]->Border) { 243 t->Complete = GL_FALSE; 244 return; 245 } 246 } 247 } 248 249 /* Test things which depend on number of texture image dimensions */ 250 if (t->Dimensions==1) { 251 /* Test 1-D mipmaps */ 252 GLuint width = t->Image[0]->Width2; 253 for (i=1; i<MAX_TEXTURE_LEVELS; i++) { 254 if (width>1) { 255 width /= 2; 256 } 257 if (!t->Image[i]) { 258 t->Complete = GL_FALSE; 259 return; 260 } 261 if (!t->Image[i]->Data) { 262 t->Complete = GL_FALSE; 263 return; 264 } 265 if (t->Image[i]->Format != t->Image[0]->Format) { 266 t->Complete = GL_FALSE; 267 return; 268 } 269 if (t->Image[i]->Border != t->Image[0]->Border) { 270 t->Complete = GL_FALSE; 271 return; 272 } 273 if (t->Image[i]->Width2 != width ) { 274 t->Complete = GL_FALSE; 275 return; 276 } 277 if (width==1) { 278 return; /* found smallest needed mipmap, all done! */ 279 } 280 } 281 } 282 else if (t->Dimensions==2) { 283 /* Test 2-D mipmaps */ 284 GLuint width = t->Image[0]->Width2; 285 GLuint height = t->Image[0]->Height2; 286 for (i=1; i<MAX_TEXTURE_LEVELS; i++) { 287 if (width>1) { 288 width /= 2; 289 } 290 if (height>1) { 291 height /= 2; 292 } 293 if (!t->Image[i]) { 294 t->Complete = GL_FALSE; 295 return; 296 } 297 if (t->Image[i]->Width2 != width) { 298 t->Complete = GL_FALSE; 299 return; 300 } 301 if (t->Image[i]->Height2 != height) { 302 t->Complete = GL_FALSE; 303 return; 304 } 305 if (width==1 && height==1) { 306 return; /* found smallest needed mipmap, all done! */ 307 } 308 } 309 } 310 else { 311 /* Dimensions = ??? */ 312 gl_problem(NULL, "Bug in gl_test_texture_object_completeness\n"); 313 } 314 } 315 } 316 317 318 319 /* 320 * Execute glGenTextures 321 */ 322 void gl_GenTextures( GLcontext *ctx, GLsizei n, GLuint *texName ) 323 { 324 GLuint first, i; 325 326 if (INSIDE_BEGIN_END(ctx)) { 327 gl_error( ctx, GL_INVALID_OPERATION, "glGenTextures" ); 328 return; 329 } 330 if (n<0) { 331 gl_error( ctx, GL_INVALID_VALUE, "glGenTextures" ); 332 return; 333 } 334 335 first = HashFindFreeKeyBlock(ctx->Shared->TexObjects, n); 336 337 /* Return the texture names */ 338 for (i=0;i<n;i++) { 339 texName[i] = first + i; 340 } 341 342 /* Allocate new, empty texture objects */ 343 for (i=0;i<n;i++) { 344 GLuint name = first + i; 345 GLuint dims = 0; 346 struct gl_texture_object *newTexObj = gl_alloc_texture_object(ctx->Shared, name, dims); 347 (void)newTexObj; 348 } 349 } 350 351 352 353 /* 354 * Execute glDeleteTextures 355 */ 356 void gl_DeleteTextures( GLcontext *ctx, GLsizei n, const GLuint *texName) 357 { 358 GLuint i; 359 360 if (INSIDE_BEGIN_END(ctx)) { 361 gl_error( ctx, GL_INVALID_OPERATION, "glAreTexturesResident" ); 362 return; 363 } 364 365 for (i=0;i<n;i++) { 366 struct gl_texture_object *t; 367 if (texName[i]>0) { 368 t = (struct gl_texture_object *) 369 HashLookup(ctx->Shared->TexObjects, texName[i]); 370 if (t) { 371 if (ctx->Texture.Current1D==t) { 372 /* revert to default 1-D texture */ 373 ctx->Texture.Current1D = ctx->Shared->Default1D; 374 t->RefCount--; 375 assert( t->RefCount >= 0 ); 376 } 377 else if (ctx->Texture.Current2D==t) { 378 /* revert to default 2-D texture */ 379 ctx->Texture.Current2D = ctx->Shared->Default2D; 380 t->RefCount--; 381 assert( t->RefCount >= 0 ); 382 } 383 384 /* tell device driver to delete texture */ 385 if (ctx->Driver.DeleteTexture) { 386 (*ctx->Driver.DeleteTexture)( ctx, t ); 387 } 388 389 if (t->RefCount==0) { 390 gl_free_texture_object(ctx->Shared, t); 391 } 392 } 393 } 394 } 395 } 396 397 398 399 /* 400 * Execute glBindTexture 401 */ 402 void gl_BindTexture( GLcontext *ctx, GLenum target, GLuint texName ) 403 { 404 struct gl_texture_object *oldTexObj; 405 struct gl_texture_object *newTexObj; 406 struct gl_texture_object **targetPointer; 407 GLuint targetDimensions; 408 409 if (INSIDE_BEGIN_END(ctx)) { 410 gl_error( ctx, GL_INVALID_OPERATION, "glAreTexturesResident" ); 411 return; 412 } 413 switch (target) { 414 case GL_TEXTURE_1D: 415 oldTexObj = ctx->Texture.Current1D; 416 targetPointer = &ctx->Texture.Current1D; 417 targetDimensions = 1; 418 break; 419 case GL_TEXTURE_2D: 420 oldTexObj = ctx->Texture.Current2D; 421 targetPointer = &ctx->Texture.Current2D; 422 targetDimensions = 2; 423 break; 424 default: 425 gl_error( ctx, GL_INVALID_ENUM, "glBindTexture" ); 426 return; 427 } 428 429 if (texName==0) { 430 /* use default n-D texture */ 431 switch (target) { 432 case GL_TEXTURE_1D: 433 newTexObj = ctx->Shared->Default1D; 434 break; 435 case GL_TEXTURE_2D: 436 newTexObj = ctx->Shared->Default2D; 437 break; 438 default: 439 gl_problem(ctx, "Bad target in gl_BindTexture"); 440 return; 441 } 442 } 443 else { 444 newTexObj = (struct gl_texture_object *) 445 HashLookup(ctx->Shared->TexObjects, texName); 446 if (newTexObj) { 447 if (newTexObj->Dimensions == 0) { 448 /* first time bound */ 449 newTexObj->Dimensions = targetDimensions; 450 } 451 else if (newTexObj->Dimensions != targetDimensions) { 452 /* wrong dimensionality */ 453 gl_error( ctx, GL_INVALID_OPERATION, "glBindTextureEXT" ); 454 return; 455 } 456 } 457 else { 458 /* create new texture object */ 459 newTexObj = gl_alloc_texture_object(ctx->Shared, texName, 460 targetDimensions); 461 } 462 } 463 464 /* Update the Texture.Current[123]D pointer */ 465 *targetPointer = newTexObj; 466 467 /* Tidy up reference counting */ 468 if (*targetPointer != oldTexObj && oldTexObj->Name>0) { 469 /* decrement reference count of the prev texture object */ 470 oldTexObj->RefCount--; 471 assert( oldTexObj->RefCount >= 0 ); 472 } 473 474 if (newTexObj->Name>0) { 475 newTexObj->RefCount++; 476 } 477 478 /* Check if we may have to use a new triangle rasterizer */ 479 if ( oldTexObj->WrapS != newTexObj->WrapS 480 || oldTexObj->WrapT != newTexObj->WrapT 481 || oldTexObj->WrapR != newTexObj->WrapR 482 || oldTexObj->MinFilter != newTexObj->MinFilter 483 || oldTexObj->MagFilter != newTexObj->MagFilter 484 || (oldTexObj->Image[0] && newTexObj->Image[0] && 485 (oldTexObj->Image[0]->Format!=newTexObj->Image[0]->Format)) 486 || !newTexObj->Complete) { 487 ctx->NewState |= NEW_RASTER_OPS; 488 } 489 490 /* If we've changed the Current[123]D texture object then update the 491 * ctx->Texture.Current pointer to point to the new texture object. 492 */ 493 if (oldTexObj==ctx->Texture.Current) { 494 ctx->Texture.Current = newTexObj; 495 } 496 497 /* The current n-D texture object can never be NULL! */ 498 assert(*targetPointer); 499 500 /* Pass BindTexture call to device driver */ 501 if (ctx->Driver.BindTexture) { 502 (*ctx->Driver.BindTexture)( ctx, target, newTexObj ); 503 } 504 } 505 506 507 508 /* 509 * Execute glPrioritizeTextures 510 */ 511 void gl_PrioritizeTextures( GLcontext *ctx, 512 GLsizei n, const GLuint *texName, 513 const GLclampf *priorities ) 514 { 515 GLuint i; 516 517 if (INSIDE_BEGIN_END(ctx)) { 518 gl_error( ctx, GL_INVALID_OPERATION, "glAreTexturesResident" ); 519 return; 520 } 521 if (n<0) { 522 gl_error( ctx, GL_INVALID_VALUE, "glAreTexturesResident(n)" ); 523 return; 524 } 525 526 for (i=0;i<n;i++) { 527 struct gl_texture_object *t; 528 if (texName[i]>0) { 529 t = (struct gl_texture_object *) 530 HashLookup(ctx->Shared->TexObjects, texName[i]); 531 if (t) { 532 t->Priority = CLAMP( priorities[i], 0.0F, 1.0F ); 533 } 534 } 535 } 536 } 537 538 539 540 /* 541 * Execute glAreTexturesResident 542 */ 543 GLboolean gl_AreTexturesResident( GLcontext *ctx, GLsizei n, 544 const GLuint *texName, 545 GLboolean *residences ) 546 { 547 GLboolean resident = GL_TRUE; 548 GLuint i; 549 550 if (INSIDE_BEGIN_END(ctx)) { 551 gl_error( ctx, GL_INVALID_OPERATION, "glAreTexturesResident" ); 552 return GL_FALSE; 553 } 554 if (n<0) { 555 gl_error( ctx, GL_INVALID_VALUE, "glAreTexturesResident(n)" ); 556 return GL_FALSE; 557 } 558 559 for (i=0;i<n;i++) { 560 struct gl_texture_object *t; 561 if (texName[i]==0) { 562 gl_error( ctx, GL_INVALID_VALUE, "glAreTexturesResident(textures)" ); 563 return GL_FALSE; 564 } 565 t = (struct gl_texture_object *) 566 HashLookup(ctx->Shared->TexObjects, texName[i]); 567 if (t) { 568 /* we consider all valid texture objects to be resident */ 569 residences[i] = GL_TRUE; 570 } 571 else { 572 gl_error( ctx, GL_INVALID_VALUE, "glAreTexturesResident(textures)" ); 573 return GL_FALSE; 574 } 575 } 576 return resident; 577 } 578 579 580 581 /* 582 * Execute glIsTexture 583 */ 584 GLboolean gl_IsTexture( GLcontext *ctx, GLuint texture ) 585 { 586 if (INSIDE_BEGIN_END(ctx)) { 587 gl_error( ctx, GL_INVALID_OPERATION, "glIsTextures" ); 588 return GL_FALSE; 589 } 590 if (texture>0 && HashLookup(ctx->Shared->TexObjects, texture)) { 591 return GL_TRUE; 592 } 593 else { 594 return GL_FALSE; 595 } 596 } 597 598